trigger.cpp 30 KB

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