trigger.cpp 26 KB

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