trigger.cpp

Engine/source/T3D/trigger.cpp

More...

Public Functions

ConsoleDocClass(Trigger , "@brief A <a href="/coding/class/classtrigger/">Trigger</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> volume of space that initiates script callbacks " "when objects pass through the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Trigger.\n\n</a>" "<a href="/coding/class/structtriggerdata/">TriggerData</a> provides the callbacks <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classtrigger/">Trigger</a> when an object enters, stays inside " "or leaves the <a href="/coding/class/classtrigger/">Trigger</a> 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
ConsoleDocClass(TriggerData , "@brief Defines shared properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" "The primary focus of the <a href="/coding/class/structtriggerdata/">TriggerData</a> datablock is the callbacks it provides when an object is " "within or leaves the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Trigger.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleSetType(TypeTriggerPolyhedron )
ConsoleType(floatList , TypeTriggerPolyhedron , Polyhedron , "" )
DefineEngineMethod(Trigger , getNumObjects , S32 , () , "@brief Get the number of objects that are within the <a href="/coding/class/classtrigger/">Trigger</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n\n</a>" "@see getObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(Trigger , getObject , S32 , (S32 index) , "@brief Retrieve the requested object that is within the <a href="/coding/class/classtrigger/">Trigger</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n\n</a>" "@param index Index of the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get (range is 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> getNumObjects()-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@returns The SimObjectID of the object, or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the requested index is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">invalid.\n</a>" " @see getNumObjects()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
IMPLEMENT_CALLBACK(Trigger , onAdd , void , (U32 objectId) , (objectId) , "@brief Called when the <a href="/coding/class/classtrigger/">Trigger</a> is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n\n</a>" "@param objectId the object <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> of the <a href="/coding/class/classtrigger/">Trigger</a> being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created\n</a>" )
IMPLEMENT_CALLBACK(Trigger , onRemove , void , (U32 objectId) , (objectId) , "@brief Called just before the <a href="/coding/class/classtrigger/">Trigger</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted.\n\n</a>" "@param objectId the object <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> of the <a href="/coding/class/classtrigger/">Trigger</a> being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted\n</a>" )
IMPLEMENT_CALLBACK(TriggerData , onEnterTrigger , void , (Trigger *trigger, GameBase *obj) , (trigger, obj) , "@brief Called when an object enters the volume of the <a href="/coding/class/classtrigger/">Trigger</a> instance using this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData.\n\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" "@param obj the object that entered the volume of the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instance\n</a>" )
IMPLEMENT_CALLBACK(TriggerData , onLeaveTrigger , void , (Trigger *trigger, GameBase *obj) , (trigger, obj) , "@brief Called when an object leaves the volume of the <a href="/coding/class/classtrigger/">Trigger</a> instance using this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData.\n\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" "@param obj the object that left the volume of the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instance\n</a>" )
IMPLEMENT_CALLBACK(TriggerData , onTickTrigger , void , (Trigger *trigger) , (trigger) , "@brief Called every tickPeriodMS number of milliseconds (as specified in the <a href="/coding/class/structtriggerdata/">TriggerData</a>) whenever " "one or more objects are inside the volume of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">trigger.\n\n</a>" "The <a href="/coding/class/classtrigger/">Trigger</a> has methods <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> retrieve the objects that are within the <a href="/coding/class/classtrigger/">Trigger</a>'s bounds <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you " "want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> do something with them in this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback.\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">inside\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tickPeriodMS\n</a>" "@see Trigger::getNumObjects()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@see <a href="/coding/class/classtrigger/#classtrigger_1ad49a7d5affe8f08e522d634fce71b648">Trigger::getObject</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(Trigger , "@brief A <a href="/coding/class/classtrigger/">Trigger</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> volume of space that initiates script callbacks " "when objects pass through the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Trigger.\n\n</a>" "<a href="/coding/class/structtriggerdata/">TriggerData</a> provides the callbacks <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/class/classtrigger/">Trigger</a> when an object enters, stays inside " "or leaves the <a href="/coding/class/classtrigger/">Trigger</a> 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

ConsoleDocClass(TriggerData , "@brief Defines shared properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" "The primary focus of the <a href="/coding/class/structtriggerdata/">TriggerData</a> datablock is the callbacks it provides when an object is " "within or leaves the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Trigger.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleSetType(TypeTriggerPolyhedron )

ConsoleType(floatList , TypeTriggerPolyhedron , Polyhedron , "" )

DECLARE_STRUCT(Polyhedron )

DefineEngineMethod(Trigger , getNumObjects , S32 , () , "@brief Get the number of objects that are within the <a href="/coding/class/classtrigger/">Trigger</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n\n</a>" "@see getObject()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(Trigger , getObject , S32 , (S32 index) , "@brief Retrieve the requested object that is within the <a href="/coding/class/classtrigger/">Trigger</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bounds.\n\n</a>" "@param index Index of the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get (range is 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> getNumObjects()-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@returns The SimObjectID of the object, or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the requested index is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">invalid.\n</a>" " @see getNumObjects()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

IMPLEMENT_CALLBACK(Trigger , onAdd , void , (U32 objectId) , (objectId) , "@brief Called when the <a href="/coding/class/classtrigger/">Trigger</a> is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n\n</a>" "@param objectId the object <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> of the <a href="/coding/class/classtrigger/">Trigger</a> being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created\n</a>" )

IMPLEMENT_CALLBACK(Trigger , onRemove , void , (U32 objectId) , (objectId) , "@brief Called just before the <a href="/coding/class/classtrigger/">Trigger</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted.\n\n</a>" "@param objectId the object <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> of the <a href="/coding/class/classtrigger/">Trigger</a> being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted\n</a>" )

IMPLEMENT_CALLBACK(TriggerData , onEnterTrigger , void , (Trigger *trigger, GameBase *obj) , (trigger, obj) , "@brief Called when an object enters the volume of the <a href="/coding/class/classtrigger/">Trigger</a> instance using this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData.\n\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" "@param obj the object that entered the volume of the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instance\n</a>" )

IMPLEMENT_CALLBACK(TriggerData , onLeaveTrigger , void , (Trigger *trigger, GameBase *obj) , (trigger, obj) , "@brief Called when an object leaves the volume of the <a href="/coding/class/classtrigger/">Trigger</a> instance using this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TriggerData.\n\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" "@param obj the object that left the volume of the <a href="/coding/class/classtrigger/">Trigger</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instance\n</a>" )

IMPLEMENT_CALLBACK(TriggerData , onTickTrigger , void , (Trigger *trigger) , (trigger) , "@brief Called every tickPeriodMS number of milliseconds (as specified in the <a href="/coding/class/structtriggerdata/">TriggerData</a>) whenever " "one or more objects are inside the volume of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">trigger.\n\n</a>" "The <a href="/coding/class/classtrigger/">Trigger</a> has methods <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> retrieve the objects that are within the <a href="/coding/class/classtrigger/">Trigger</a>'s bounds <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you " "want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> do something with them in this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback.\n</a>" "@param trigger the <a href="/coding/class/classtrigger/">Trigger</a> instance whose volume the object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">inside\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tickPeriodMS\n</a>" "@see Trigger::getNumObjects()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@see <a href="/coding/class/classtrigger/#classtrigger_1ad49a7d5affe8f08e522d634fce71b648">Trigger::getObject</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(TriggerData )

IMPLEMENT_CO_NETOBJECT_V1(Trigger )

IMPLEMENT_STRUCT(Polyhedron , Polyhedron , "" )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "T3D/trigger.h"
 26
 27#include "scene/sceneRenderState.h"
 28#include "console/consoleTypes.h"
 29#include "console/engineAPI.h"
 30#include "collision/boxConvex.h"
 31
 32#include "core/stream/bitStream.h"
 33#include "math/mathIO.h"
 34#include "gfx/gfxTransformSaver.h"
 35#include "renderInstance/renderPassManager.h"
 36#include "gfx/gfxDrawUtil.h"
 37#include "T3D/physics/physicsPlugin.h"
 38#include "T3D/physics/physicsBody.h"
 39#include "T3D/physics/physicsCollision.h"
 40
 41
 42bool Trigger::smRenderTriggers = false;
 43
 44//-----------------------------------------------------------------------------
 45
 46//----------------------------------------------------------------------------
 47
 48IMPLEMENT_CO_DATABLOCK_V1(TriggerData);
 49
 50ConsoleDocClass( TriggerData,
 51   "@brief Defines shared properties for Trigger objects.\n\n"
 52
 53   "The primary focus of the TriggerData datablock is the callbacks it provides when an object is "
 54   "within or leaves the Trigger bounds.\n"
 55
 56   "@see Trigger.\n"
 57   "@ingroup gameObjects\n"
 58   "@ingroup Datablocks\n"
 59);
 60
 61IMPLEMENT_CALLBACK( TriggerData, onEnterTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ),
 62   "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
 63
 64   "@param trigger the Trigger instance whose volume the object entered\n"
 65   "@param obj the object that entered the volume of the Trigger instance\n" );
 66
 67IMPLEMENT_CALLBACK( TriggerData, onTickTrigger, void, ( Trigger* trigger ), ( trigger ),
 68   "@brief Called every tickPeriodMS number of milliseconds (as specified in the TriggerData) whenever "
 69   "one or more objects are inside the volume of the trigger.\n\n"
 70
 71   "The Trigger has methods to retrieve the objects that are within the Trigger's bounds if you "
 72   "want to do something with them in this callback.\n"
 73
 74   "@param trigger the Trigger instance whose volume the object is inside\n"
 75   
 76   "@see tickPeriodMS\n"
 77   "@see Trigger::getNumObjects()\n"
 78   "@see Trigger::getObject()\n");
 79
 80IMPLEMENT_CALLBACK( TriggerData, onLeaveTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ),
 81   "@brief Called when an object leaves the volume of the Trigger instance using this TriggerData.\n\n"
 82
 83   "@param trigger the Trigger instance whose volume the object left\n"
 84   "@param obj the object that left the volume of the Trigger instance\n" );
 85
 86TriggerData::TriggerData()
 87{
 88   tickPeriodMS = 100;
 89   isClientSide = false;
 90}
 91
 92bool TriggerData::onAdd()
 93{
 94   if (!Parent::onAdd())
 95      return false;
 96
 97   return true;
 98}
 99
100void TriggerData::initPersistFields()
101{
102   addGroup("Callbacks");
103
104      addField( "tickPeriodMS",  TypeS32,    Offset( tickPeriodMS, TriggerData ),
105         "@brief Time in milliseconds between calls to onTickTrigger() while at least one object is within a Trigger's bounds.\n\n"
106         "@see onTickTrigger()\n");
107      addField( "clientSide",    TypeBool,   Offset( isClientSide, TriggerData ),
108         "Forces Trigger callbacks to only be called on clients.");
109
110   endGroup("Callbacks");
111
112   Parent::initPersistFields();
113}
114
115
116//--------------------------------------------------------------------------
117void TriggerData::packData(BitStream* stream)
118{
119   Parent::packData(stream);
120   stream->write(tickPeriodMS);
121   stream->write(isClientSide);
122}
123
124void TriggerData::unpackData(BitStream* stream)
125{
126   Parent::unpackData(stream);
127   stream->read(&tickPeriodMS);
128   stream->read(&isClientSide);
129}
130
131
132//--------------------------------------------------------------------------
133
134IMPLEMENT_CO_NETOBJECT_V1(Trigger);
135
136ConsoleDocClass( Trigger,
137   "@brief A Trigger is a volume of space that initiates script callbacks "
138   "when objects pass through the Trigger.\n\n"
139
140   "TriggerData provides the callbacks for the Trigger when an object enters, stays inside "
141   "or leaves the Trigger's volume.\n\n"
142
143   "@see TriggerData\n"
144   "@ingroup gameObjects\n"
145);
146
147IMPLEMENT_CALLBACK( Trigger, onAdd, void, ( U32 objectId ), ( objectId ),
148   "@brief Called when the Trigger is being created.\n\n"
149   "@param objectId the object id of the Trigger being created\n" );
150
151IMPLEMENT_CALLBACK( Trigger, onRemove, void, ( U32 objectId ), ( objectId ),
152   "@brief Called just before the Trigger is deleted.\n\n"
153   "@param objectId the object id of the Trigger being deleted\n" );
154
155Trigger::Trigger()
156{
157   // Don't ghost by default.
158   mNetFlags.set(Ghostable | ScopeAlways);
159
160   mTypeMask |= TriggerObjectType;
161
162   mObjScale.set(1, 1, 1);
163   mObjToWorld.identity();
164   mWorldToObj.identity();
165
166   mDataBlock = NULL;
167
168   mLastThink = 0;
169   mCurrTick  = 0;
170
171   mConvexList = new Convex;
172
173   mPhysicsRep = NULL;
174   mTripOnce = false;
175   mTrippedBy = 0xFFFFFFFF;
176   mTripCondition = "";
177}
178
179Trigger::~Trigger()
180{
181   delete mConvexList;
182   mConvexList = NULL;
183   SAFE_DELETE( mPhysicsRep );
184}
185
186bool Trigger::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
187{
188   // Collide against bounding box
189   F32 st,et,fst = 0,fet = 1;
190   F32 *bmin = &mObjBox.minExtents.x;
191   F32 *bmax = &mObjBox.maxExtents.x;
192   F32 const *si = &start.x;
193   F32 const *ei = &end.x;
194
195   for (S32 i = 0; i < 3; i++)
196   {
197      if (*si < *ei)
198      {
199         if (*si > *bmax || *ei < *bmin)
200            return false;
201         F32 di = *ei - *si;
202         st = (*si < *bmin)? (*bmin - *si) / di: 0;
203         et = (*ei > *bmax)? (*bmax - *si) / di: 1;
204      }
205      else
206      {
207         if (*ei > *bmax || *si < *bmin)
208            return false;
209         F32 di = *ei - *si;
210         st = (*si > *bmax)? (*bmax - *si) / di: 0;
211         et = (*ei < *bmin)? (*bmin - *si) / di: 1;
212      }
213      if (st > fst) fst = st;
214      if (et < fet) fet = et;
215      if (fet < fst)
216         return false;
217      bmin++; bmax++;
218      si++; ei++;
219   }
220
221   info->normal = start - end;
222   info->normal.normalizeSafe();
223   getTransform().mulV( info->normal );
224
225   info->t = fst;
226   info->object = this;
227   info->point.interpolate(start,end,fst);
228   info->material = 0;
229   return true;
230}
231
232
233//--------------------------------------------------------------------------
234/* Console polyhedron data type exporter
235   The polyhedron type is really a quadrilateral and consists of a corner
236   point follow by three vectors representing the edges extending from the
237   corner.
238*/
239DECLARE_STRUCT( Polyhedron );
240IMPLEMENT_STRUCT( Polyhedron, Polyhedron,,
241   "" )
242
243   FIELD(mPointList, pointList, 1, "")
244   FIELD(mPlaneList, planeList, 1, "")
245   FIELD(mEdgeList, edgeList, 1, "")
246
247END_IMPLEMENT_STRUCT;
248ConsoleType(floatList, TypeTriggerPolyhedron, Polyhedron, "")
249
250
251ConsoleGetType( TypeTriggerPolyhedron )
252{
253   U32 i;
254   Polyhedron* pPoly = reinterpret_cast<Polyhedron*>(dptr);
255
256   // First point is corner, need to find the three vectors...`
257   Point3F origin = pPoly->mPointList[0];
258   U32 currVec = 0;
259   Point3F vecs[3];
260   for (i = 0; i < pPoly->mEdgeList.size(); i++) {
261      const U32 *vertex = pPoly->mEdgeList[i].vertex;
262      if (vertex[0] == 0)
263         vecs[currVec++] = pPoly->mPointList[vertex[1]] - origin;
264      else
265         if (vertex[1] == 0)
266            vecs[currVec++] = pPoly->mPointList[vertex[0]] - origin;
267   }
268   AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron");
269
270   // Build output string.
271   static const U32 bufSize = 1024;
272   char* retBuf = Con::getReturnBuffer(bufSize);
273   dSprintf(retBuf, bufSize, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f",
274            origin.x, origin.y, origin.z,
275            vecs[0].x, vecs[0].y, vecs[0].z,
276            vecs[2].x, vecs[2].y, vecs[2].z,
277            vecs[1].x, vecs[1].y, vecs[1].z);
278            
279
280   return retBuf;
281}
282
283/* Console polyhedron data type loader
284   The polyhedron type is really a quadrilateral and consists of an corner
285   point follow by three vectors representing the edges extending from the
286   corner.
287*/
288ConsoleSetType( TypeTriggerPolyhedron )
289{
290   if (argc != 1) {
291      Con::printf("(TypeTriggerPolyhedron) multiple args not supported for polyhedra");
292      return;
293   }
294
295   Point3F origin;
296   Point3F vecs[3];
297
298   U32 numArgs = dSscanf(argv[0], "%g %g %g %g %g %g %g %g %g %g %g %g",
299                         &origin.x, &origin.y, &origin.z,
300                         &vecs[0].x, &vecs[0].y, &vecs[0].z,
301                         &vecs[1].x, &vecs[1].y, &vecs[1].z,
302                         &vecs[2].x, &vecs[2].y, &vecs[2].z);
303   if (numArgs != 12) {
304      Con::printf("Bad polyhedron!");
305      return;
306   }
307
308   Polyhedron* pPoly = reinterpret_cast<Polyhedron*>(dptr);
309
310   // This setup goes against conventions for Polyhedrons in that it a) sets up
311   // edges with CCW instead of CW order for face[0] and that it b) lets plane
312   // normals face outwards rather than inwards.
313
314   pPoly->mPointList.setSize(8);
315   pPoly->mPointList[0] = origin;
316   pPoly->mPointList[1] = origin + vecs[0];
317   pPoly->mPointList[2] = origin + vecs[1];
318   pPoly->mPointList[3] = origin + vecs[2];
319   pPoly->mPointList[4] = origin + vecs[0] + vecs[1];
320   pPoly->mPointList[5] = origin + vecs[0] + vecs[2];
321   pPoly->mPointList[6] = origin + vecs[1] + vecs[2];
322   pPoly->mPointList[7] = origin + vecs[0] + vecs[1] + vecs[2];
323
324   Point3F normal;
325   pPoly->mPlaneList.setSize(6);
326
327   mCross(vecs[2], vecs[0], &normal);
328   pPoly->mPlaneList[0].set(origin, normal);
329   mCross(vecs[0], vecs[1], &normal);
330   pPoly->mPlaneList[1].set(origin, normal);
331   mCross(vecs[1], vecs[2], &normal);
332   pPoly->mPlaneList[2].set(origin, normal);
333   mCross(vecs[1], vecs[0], &normal);
334   pPoly->mPlaneList[3].set(pPoly->mPointList[7], normal);
335   mCross(vecs[2], vecs[1], &normal);
336   pPoly->mPlaneList[4].set(pPoly->mPointList[7], normal);
337   mCross(vecs[0], vecs[2], &normal);
338   pPoly->mPlaneList[5].set(pPoly->mPointList[7], normal);
339
340   pPoly->mEdgeList.setSize(12);
341   pPoly->mEdgeList[0].vertex[0]  = 0; pPoly->mEdgeList[0].vertex[1]  = 1; pPoly->mEdgeList[0].face[0]  = 0; pPoly->mEdgeList[0].face[1]  = 1;
342   pPoly->mEdgeList[1].vertex[0]  = 1; pPoly->mEdgeList[1].vertex[1]  = 5; pPoly->mEdgeList[1].face[0]  = 0; pPoly->mEdgeList[1].face[1]  = 4;
343   pPoly->mEdgeList[2].vertex[0]  = 5; pPoly->mEdgeList[2].vertex[1]  = 3; pPoly->mEdgeList[2].face[0]  = 0; pPoly->mEdgeList[2].face[1]  = 3;
344   pPoly->mEdgeList[3].vertex[0]  = 3; pPoly->mEdgeList[3].vertex[1]  = 0; pPoly->mEdgeList[3].face[0]  = 0; pPoly->mEdgeList[3].face[1]  = 2;
345   pPoly->mEdgeList[4].vertex[0]  = 3; pPoly->mEdgeList[4].vertex[1]  = 6; pPoly->mEdgeList[4].face[0]  = 3; pPoly->mEdgeList[4].face[1]  = 2;
346   pPoly->mEdgeList[5].vertex[0]  = 6; pPoly->mEdgeList[5].vertex[1]  = 2; pPoly->mEdgeList[5].face[0]  = 2; pPoly->mEdgeList[5].face[1]  = 5;
347   pPoly->mEdgeList[6].vertex[0]  = 2; pPoly->mEdgeList[6].vertex[1]  = 0; pPoly->mEdgeList[6].face[0]  = 2; pPoly->mEdgeList[6].face[1]  = 1;
348   pPoly->mEdgeList[7].vertex[0]  = 1; pPoly->mEdgeList[7].vertex[1]  = 4; pPoly->mEdgeList[7].face[0]  = 4; pPoly->mEdgeList[7].face[1]  = 1;
349   pPoly->mEdgeList[8].vertex[0]  = 4; pPoly->mEdgeList[8].vertex[1]  = 2; pPoly->mEdgeList[8].face[0]  = 1; pPoly->mEdgeList[8].face[1]  = 5;
350   pPoly->mEdgeList[9].vertex[0]  = 4; pPoly->mEdgeList[9].vertex[1]  = 7; pPoly->mEdgeList[9].face[0]  = 4; pPoly->mEdgeList[9].face[1]  = 5;
351   pPoly->mEdgeList[10].vertex[0] = 5; pPoly->mEdgeList[10].vertex[1] = 7; pPoly->mEdgeList[10].face[0] = 3; pPoly->mEdgeList[10].face[1] = 4;
352   pPoly->mEdgeList[11].vertex[0] = 7; pPoly->mEdgeList[11].vertex[1] = 6; pPoly->mEdgeList[11].face[0] = 3; pPoly->mEdgeList[11].face[1] = 5;
353}
354
355
356//-----------------------------------------------------------------------------
357void Trigger::consoleInit()
358{
359   Con::addVariable( "$Trigger::renderTriggers", TypeBool, &smRenderTriggers,
360      "@brief Forces all Trigger's to render.\n\n"
361      "Used by the Tools and debug render modes.\n"
362      "@ingroup gameObjects" );
363}
364
365void Trigger::initPersistFields()
366{
367   addField("polyhedron", TypeTriggerPolyhedron, Offset(mTriggerPolyhedron, Trigger),
368      "@brief Defines a non-rectangular area for the trigger.\n\n"
369      "Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral "
370      "trigger area.  The quadrilateral is defined as a corner point followed by three vectors "
371      "representing the edges extending from the corner.\n");
372
373   addField("TripOnce", TypeBool, Offset(mTripOnce, Trigger),"Do we trigger callacks just the once?");
374   addField("TripCondition", TypeRealString, Offset(mTripCondition, Trigger),"evaluation condition to trip callbacks (true/false)");
375   addField("TrippedBy", TypeS32, Offset(mTrippedBy, Trigger), "typemask filter");
376   addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, Trigger), &setEnterCmd, &defaultProtectedGetFn,
377      "The command to execute when an object enters this trigger. Object id stored in %%obj. Maximum 1023 characters." );
378   addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, Trigger), &setLeaveCmd, &defaultProtectedGetFn,
379      "The command to execute when an object leaves this trigger. Object id stored in %%obj. Maximum 1023 characters." );
380   addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, Trigger), &setTickCmd, &defaultProtectedGetFn,
381      "The command to execute while an object is inside this trigger. Maximum 1023 characters." );
382
383   Parent::initPersistFields();
384}
385
386bool Trigger::setEnterCmd( void *object, const char *index, const char *data )
387{
388   static_cast<Trigger*>(object)->setMaskBits(EnterCmdMask);
389   return true; // to update the actual field
390}
391
392bool Trigger::setLeaveCmd(void *object, const char *index, const char *data)
393{
394   static_cast<Trigger*>(object)->setMaskBits(LeaveCmdMask);
395   return true; // to update the actual field
396}
397
398bool Trigger::setTickCmd(void *object, const char *index, const char *data)
399{
400   static_cast<Trigger*>(object)->setMaskBits(TickCmdMask);
401   return true; // to update the actual field
402}
403
404//------------------------------------------------------------------------------
405
406void Trigger::testObjects()
407{
408   Vector<SceneObject*> foundobjs;
409   foundobjs.clear();
410   if (getSceneManager() && getSceneManager()->getContainer() && getSceneManager()->getZoneManager())
411      getSceneManager()->getContainer()->findObjectList(getWorldBox(), mTrippedBy, &foundobjs);
412   else return;
413
414   for (S32 i = 0; i < foundobjs.size(); i++)
415   {
416      GameBase* so = dynamic_cast<GameBase*>(foundobjs[i]);
417      if (so)
418         potentialEnterObject(so);
419   }
420}
421
422//--------------------------------------------------------------------------
423
424bool Trigger::onAdd()
425{
426   if(!Parent::onAdd())
427      return false;
428
429   onAdd_callback( getId() );
430
431   Polyhedron temp = mTriggerPolyhedron;
432   setTriggerPolyhedron(temp);
433   mTripped = false;
434   addToScene();
435
436   if (isServerObject())
437      scriptOnAdd();
438
439   testObjects();
440
441   return true;
442}
443
444void Trigger::onRemove()
445{
446   onRemove_callback( getId() );
447
448   mConvexList->nukeList();
449
450   removeFromScene();
451   Parent::onRemove();
452}
453
454bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload )
455{
456   mDataBlock = dynamic_cast<TriggerData*>( dptr );
457   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
458      return false;
459
460   scriptOnNewDataBlock();
461   return true;
462}
463
464void Trigger::onDeleteNotify( SimObject *obj )
465{
466   GameBase* pScene = dynamic_cast<GameBase*>( obj );
467
468   if  ( pScene != NULL && mDataBlock != NULL )
469   {
470      for ( U32 i = 0; i < mObjects.size(); i++ )
471      {
472         if ( pScene == mObjects[i] )
473         {
474            mObjects.erase(i);
475            if (mDataBlock)
476               mDataBlock->onLeaveTrigger_callback( this, NULL );
477            break;
478         }
479      }
480   }
481
482   Parent::onDeleteNotify( obj );
483}
484
485void Trigger::inspectPostApply()
486{
487   setTriggerPolyhedron(mTriggerPolyhedron);
488   setMaskBits(PolyMask);
489   Parent::inspectPostApply();
490}
491
492//--------------------------------------------------------------------------
493
494void Trigger::buildConvex(const Box3F& box, Convex* convex)
495{
496   // These should really come out of a pool
497   mConvexList->collectGarbage();
498
499   Box3F realBox = box;
500   mWorldToObj.mul(realBox);
501   realBox.minExtents.convolveInverse(mObjScale);
502   realBox.maxExtents.convolveInverse(mObjScale);
503
504   if (realBox.isOverlapped(getObjBox()) == false)
505      return;
506
507   // Just return a box convex for the entire shape...
508   Convex* cc = 0;
509   CollisionWorkingList& wl = convex->getWorkingList();
510   for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
511      if (itr->mConvex->getType() == BoxConvexType &&
512          itr->mConvex->getObject() == this) {
513         cc = itr->mConvex;
514         break;
515      }
516   }
517   if (cc)
518      return;
519
520   // Create a new convex.
521   BoxConvex* cp = new BoxConvex;
522   mConvexList->registerObject(cp);
523   convex->addToWorkingList(cp);
524   cp->init(this);
525
526   mObjBox.getCenter(&cp->mCenter);
527   cp->mSize.x = mObjBox.len_x() / 2.0f;
528   cp->mSize.y = mObjBox.len_y() / 2.0f;
529   cp->mSize.z = mObjBox.len_z() / 2.0f;
530}
531
532//------------------------------------------------------------------------------
533
534void Trigger::setTransform(const MatrixF & mat)
535{
536   Parent::setTransform(mat);
537
538   if ( mPhysicsRep )
539      mPhysicsRep->setTransform( mat );
540
541   if (isServerObject()) {
542      MatrixF base(true);
543      base.scale(Point3F(1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.x,
544                         1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.y,
545                         1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.z));
546      base.mul(mWorldToObj);
547      mClippedList.setBaseTransform(base);
548
549      setMaskBits(TransformMask | ScaleMask);
550   }
551
552   testObjects();
553}
554
555void Trigger::onUnmount( SceneObject *obj, S32 node )
556{
557    Parent::onUnmount( obj, node );
558   // Make sure the client get's the final server pos.
559   setMaskBits(TransformMask | ScaleMask);
560}
561
562void Trigger::prepRenderImage( SceneRenderState *state )
563{
564   // only render if selected or render flag is set
565   if ( !smRenderTriggers && !isSelected() )
566      return;
567
568   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
569   ri->renderDelegate.bind( this, &Trigger::renderObject );
570   ri->type = RenderPassManager::RIT_Editor;      
571   ri->translucentSort = true;
572   ri->defaultKey = 1;
573   state->getRenderPass()->addInst( ri );
574}
575
576void Trigger::renderObject( ObjectRenderInst *ri,
577                            SceneRenderState *state,
578                            BaseMatInstance *overrideMat )
579{
580   if(overrideMat)
581      return;
582
583   GFXStateBlockDesc desc;
584   desc.setZReadWrite( true, false );
585   desc.setBlend( true );
586
587   // Trigger polyhedrons are set up with outward facing normals and CCW ordering
588   // so can't enable backface culling.
589   desc.setCullMode( GFXCullNone );
590
591   GFXTransformSaver saver;
592
593   MatrixF mat = getRenderTransform();
594   mat.scale( getScale() );
595
596   GFX->multWorld( mat );
597
598   GFXDrawUtil *drawer = GFX->getDrawUtil();
599   
600   drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI( 255, 192, 0, 45 ) );
601
602   // Render wireframe.
603
604   desc.setFillModeWireframe();
605   drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI::BLACK );
606}
607
608void Trigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron)
609{
610   mTriggerPolyhedron = rPolyhedron;
611
612   if (mTriggerPolyhedron.mPointList.size() != 0) {
613      mObjBox.minExtents.set(1e10, 1e10, 1e10);
614      mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
615      for (U32 i = 0; i < mTriggerPolyhedron.mPointList.size(); i++) {
616         mObjBox.minExtents.setMin(mTriggerPolyhedron.mPointList[i]);
617         mObjBox.maxExtents.setMax(mTriggerPolyhedron.mPointList[i]);
618      }
619   } else {
620      mObjBox.minExtents.set(-0.5, -0.5, -0.5);
621      mObjBox.maxExtents.set( 0.5,  0.5,  0.5);
622   }
623
624   MatrixF xform = getTransform();
625   setTransform(xform);
626
627   mClippedList.clear();
628   mClippedList.mPlaneList = mTriggerPolyhedron.mPlaneList;
629//   for (U32 i = 0; i < mClippedList.mPlaneList.size(); i++)
630//      mClippedList.mPlaneList[i].neg();
631
632   MatrixF base(true);
633   base.scale(Point3F(1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.x,
634                      1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.y,
635                      1.0/<a href="/coding/class/classsceneobject/#classsceneobject_1abf78914dea349b0201b66591567c6d91">mObjScale</a>.z));
636   base.mul(mWorldToObj);
637
638   mClippedList.setBaseTransform(base);
639
640   SAFE_DELETE( mPhysicsRep );
641
642   if ( PHYSICSMGR )
643   {
644      PhysicsCollision *colShape = PHYSICSMGR->createCollision();
645
646      MatrixF colMat( true );      
647      colMat.displace( Point3F( 0, 0, mObjBox.getExtents().z * 0.5f * mObjScale.z ) );
648      
649      colShape->addBox( mObjBox.getExtents() * 0.5f * mObjScale, colMat );
650      //MatrixF colMat( true );
651      //colMat.scale( mObjScale );
652      //colShape->addConvex( mTriggerPolyhedron.pointList.address(), mTriggerPolyhedron.pointList.size(), colMat );
653
654      PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
655      mPhysicsRep = PHYSICSMGR->createBody();
656      mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
657      mPhysicsRep->setTransform( getTransform() );
658   }
659}
660
661
662//--------------------------------------------------------------------------
663
664bool Trigger::testObject(GameBase* enter)
665{
666   if (mTriggerPolyhedron.mPointList.size() == 0)
667      return false;
668
669   if (!(enter->getTypeMask() & mTrippedBy))
670      return false; //not the right type of object
671   
672   mClippedList.clear();
673
674   SphereF sphere;
675   sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
676   VectorF bv = mWorldBox.maxExtents - sphere.center;
677   sphere.radius = bv.len();
678
679   enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere);
680   return mClippedList.isEmpty() == false;
681}
682
683bool Trigger::testTrippable()
684{
685   if ((mTripOnce == true) && (mTripped == true))
686      return false; // we've already fired the once
687   return true;
688}
689
690bool Trigger::testCondition()
691{
692   if (mTripCondition.isEmpty())
693      return true; //we've got no tests to run so just do it
694
695   //test the mapper plugged in condition line
696   String resVar = getIdString() + String(".result");
697   Con::setBoolVariable(resVar.c_str(), false);
698   String command = resVar + "=" + mTripCondition + ";";
699   Con::evaluatef(command.c_str());
700   if (Con::getBoolVariable(resVar.c_str()) == 1)
701   {
702      return true;
703   }
704   return false;
705}
706
707bool Trigger::evalCmD(String* cmd)
708{
709   if (!testTrippable()) return false;
710   if (cmd && cmd->isNotEmpty())//do we have a callback?
711   {
712      return testCondition();
713   }
714   return false;
715}
716
717void Trigger::potentialEnterObject(GameBase* enter)
718{
719   if( (!mDataBlock || mDataBlock->isClientSide) && isServerObject() )
720      return;
721   if( (mDataBlock && !mDataBlock->isClientSide) && isGhost() )
722      return;
723
724   for (U32 i = 0; i < mObjects.size(); i++) {
725      if (mObjects[i] == enter)
726         return;
727   }
728
729   if (testObject(enter) == true) {
730      mObjects.push_back(enter);
731      deleteNotify(enter);
732
733      if(evalCmD(&mEnterCommand))
734      {
735         String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand;
736         Con::evaluate(command.c_str());
737      }
738
739      if( mDataBlock && testTrippable() && testCondition())
740         mDataBlock->onEnterTrigger_callback( this, enter );
741      mTripped = true;
742   }
743}
744
745
746void Trigger::processTick(const Move* move)
747{
748   Parent::processTick(move);
749
750   if (!mDataBlock)
751      return;
752   if (mDataBlock->isClientSide && isServerObject())
753      return;
754   if (!mDataBlock->isClientSide && isClientObject())
755      return;
756
757   if (isMounted()) {
758         MatrixF mat;
759         mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
760         setTransform(mat);
761         setRenderTransform(mat);
762    }
763
764   //
765   if (mObjects.size() == 0)
766      return;
767
768   if (mLastThink + mDataBlock->tickPeriodMS < mCurrTick)
769   {
770      mCurrTick  = 0;
771      mLastThink = 0;
772
773      for (S32 i = S32(mObjects.size() - 1); i >= 0; i--)
774      {
775         if (testObject(mObjects[i]) == false)
776         {
777            GameBase* remove = mObjects[i];
778            mObjects.erase(i);
779            clearNotify(remove);
780            
781            if (evalCmD(&mLeaveCommand))
782            {
783               String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand;
784               Con::evaluate(command.c_str());
785            }
786            if (testTrippable() && testCondition())
787               mDataBlock->onLeaveTrigger_callback( this, remove );
788            mTripped = true;
789         }
790      }
791
792      if (evalCmD(&mTickCommand))
793         Con::evaluate(mTickCommand.c_str());
794
795      if (mObjects.size() != 0 && testTrippable() && testCondition())
796         mDataBlock->onTickTrigger_callback( this );
797   }
798   else
799   {
800      mCurrTick += TickMs;
801   }
802}
803
804void Trigger::interpolateTick(F32 delta)
805{
806   if (isMounted()) {
807      MatrixF mat;
808      mMount.object->getRenderMountTransform( delta, mMount.node, mMount.xfm, &mat );
809      setRenderTransform(mat);
810   }
811}
812
813//--------------------------------------------------------------------------
814
815U32 Trigger::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
816{
817   U32 i;
818   U32 retMask = Parent::packUpdate(con, mask, stream);
819
820   if( stream->writeFlag( mask & TransformMask ) )
821   {
822      stream->writeAffineTransform(mObjToWorld);
823   }
824
825   // Write the polyhedron
826   if( stream->writeFlag( mask & PolyMask ) )
827   {
828      stream->write(mTriggerPolyhedron.mPointList.size());
829      for (i = 0; i < mTriggerPolyhedron.mPointList.size(); i++)
830         mathWrite(*stream, mTriggerPolyhedron.mPointList[i]);
831
832      stream->write(mTriggerPolyhedron.mPlaneList.size());
833      for (i = 0; i < mTriggerPolyhedron.mPlaneList.size(); i++)
834         mathWrite(*stream, mTriggerPolyhedron.mPlaneList[i]);
835
836      stream->write(mTriggerPolyhedron.mEdgeList.size());
837      for (i = 0; i < mTriggerPolyhedron.mEdgeList.size(); i++) {
838         const Polyhedron::Edge& rEdge = mTriggerPolyhedron.mEdgeList[i];
839
840         stream->write(rEdge.face[0]);
841         stream->write(rEdge.face[1]);
842         stream->write(rEdge.vertex[0]);
843         stream->write(rEdge.vertex[1]);
844      }
845   }
846
847   if( stream->writeFlag( mask & EnterCmdMask ) )
848      stream->writeLongString(CMD_SIZE-1, mEnterCommand.c_str());
849   if( stream->writeFlag( mask & LeaveCmdMask ) )
850      stream->writeLongString(CMD_SIZE-1, mLeaveCommand.c_str());
851   if( stream->writeFlag( mask & TickCmdMask ) )
852      stream->writeLongString(CMD_SIZE-1, mTickCommand.c_str());
853
854   return retMask;
855}
856
857void Trigger::unpackUpdate(NetConnection* con, BitStream* stream)
858{
859   Parent::unpackUpdate(con, stream);
860
861   U32 i, size;
862
863   // Transform
864   if( stream->readFlag() )
865   {
866      MatrixF temp;
867      stream->readAffineTransform(&temp);
868      setTransform(temp);
869   }
870
871   // Read the polyhedron
872   if( stream->readFlag() )
873   {
874      Polyhedron tempPH;
875      stream->read(&size);
876      tempPH.mPointList.setSize(size);
877      for (i = 0; i < tempPH.mPointList.size(); i++)
878         mathRead(*stream, &tempPH.mPointList[i]);
879
880      stream->read(&size);
881      tempPH.mPlaneList.setSize(size);
882      for (i = 0; i < tempPH.mPlaneList.size(); i++)
883         mathRead(*stream, &tempPH.mPlaneList[i]);
884
885      stream->read(&size);
886      tempPH.mEdgeList.setSize(size);
887      for (i = 0; i < tempPH.mEdgeList.size(); i++) {
888         Polyhedron::Edge& rEdge = tempPH.mEdgeList[i];
889
890         stream->read(&rEdge.face[0]);
891         stream->read(&rEdge.face[1]);
892         stream->read(&rEdge.vertex[0]);
893         stream->read(&rEdge.vertex[1]);
894      }
895      setTriggerPolyhedron(tempPH);
896   }
897
898   if( stream->readFlag() )
899   {
900      char buf[CMD_SIZE];
901      stream->readLongString(CMD_SIZE-1, buf);
902      mEnterCommand = buf;
903   }
904   if( stream->readFlag() )
905   {
906      char buf[CMD_SIZE];
907      stream->readLongString(CMD_SIZE-1, buf);
908      mLeaveCommand = buf;
909   }
910   if( stream->readFlag() )
911   {
912      char buf[CMD_SIZE];
913      stream->readLongString(CMD_SIZE-1, buf);
914      mTickCommand = buf;
915   }
916}
917
918//ConsoleMethod( Trigger, getNumObjects, S32, 2, 2, "")
919DefineEngineMethod( Trigger, getNumObjects, S32, (),,
920   "@brief Get the number of objects that are within the Trigger's bounds.\n\n"
921   "@see getObject()\n")
922{
923   return object->getNumTriggeringObjects();
924}
925
926//ConsoleMethod( Trigger, getObject, S32, 3, 3, "(int idx)")
927DefineEngineMethod( Trigger, getObject, S32, ( S32 index ),,
928   "@brief Retrieve the requested object that is within the Trigger's bounds.\n\n"
929   "@param index Index of the object to get (range is 0 to getNumObjects()-1)\n"
930   "@returns The SimObjectID of the object, or -1 if the requested index is invalid.\n"
931   "@see getNumObjects()\n")
932{
933   if (index >= object->getNumTriggeringObjects() || index < 0)
934      return -1;
935   else
936      return object->getObject(U32(index))->getId();
937}
938