item.cpp 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395
  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/item.h"
  24. #include "core/stream/bitStream.h"
  25. #include "math/mMath.h"
  26. #include "console/console.h"
  27. #include "console/consoleTypes.h"
  28. #include "sim/netConnection.h"
  29. #include "collision/boxConvex.h"
  30. #include "collision/earlyOutPolyList.h"
  31. #include "collision/extrudedPolyList.h"
  32. #include "math/mPolyhedron.h"
  33. #include "math/mathIO.h"
  34. #include "lighting/lightInfo.h"
  35. #include "lighting/lightManager.h"
  36. #include "T3D/physics/physicsPlugin.h"
  37. #include "T3D/physics/physicsBody.h"
  38. #include "T3D/physics/physicsCollision.h"
  39. #include "ts/tsShapeInstance.h"
  40. #include "console/engineAPI.h"
  41. const F32 sRotationSpeed = 6.0f; // Secs/Rotation
  42. const F32 sAtRestVelocity = 0.15f; // Min speed after collision
  43. const S32 sCollisionTimeout = 15; // Timout value in ticks
  44. // Client prediction
  45. static F32 sMinWarpTicks = 0.5 ; // Fraction of tick at which instant warp occures
  46. static S32 sMaxWarpTicks = 3; // Max warp duration in ticks
  47. F32 Item::mGravity = -20.0f;
  48. const U32 sClientCollisionMask = (TerrainObjectType |
  49. StaticShapeObjectType |
  50. VehicleObjectType |
  51. PlayerObjectType);
  52. const U32 sServerCollisionMask = (sClientCollisionMask);
  53. const S32 Item::csmAtRestTimer = 64;
  54. //----------------------------------------------------------------------------
  55. IMPLEMENT_CO_DATABLOCK_V1(ItemData);
  56. ConsoleDocClass( ItemData,
  57. "@brief Stores properties for an individual Item type.\n\n"
  58. "Items represent an object in the world, usually one that the player will interact with. "
  59. "One example is a health kit on the group that is automatically picked up when the player "
  60. "comes into contact with it.\n\n"
  61. "ItemData provides the common properties for a set of Items. These properties include a "
  62. "DTS or DAE model used to render the Item in the world, its physical properties for when the "
  63. "Item interacts with the world (such as being tossed by the player), and any lights that emit "
  64. "from the Item.\n\n"
  65. "@tsexample\n"
  66. "datablock ItemData(HealthKitSmall)\n"
  67. "{\n"
  68. " category =\"Health\";\n"
  69. " className = \"HealthPatch\";\n"
  70. " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n"
  71. " gravityMod = \"1.0\";\n"
  72. " mass = 2;\n"
  73. " friction = 1;\n"
  74. " elasticity = 0.3;\n"
  75. " density = 2;\n"
  76. " drag = 0.5;\n"
  77. " maxVelocity = \"10.0\";\n"
  78. " emap = true;\n"
  79. " sticky = false;\n"
  80. " dynamicType = \"0\"\n;"
  81. " lightOnlyStatic = false;\n"
  82. " lightType = \"NoLight\";\n"
  83. " lightColor = \"1.0 1.0 1.0 1.0\";\n"
  84. " lightTime = 1000;\n"
  85. " lightRadius = 10.0;\n"
  86. " simpleServerCollision = true;"
  87. " // Dynamic properties used by the scripts\n\n"
  88. " pickupName = \"a small health kit\";\n"
  89. " repairAmount = 50;\n"
  90. "};\n"
  91. "@endtsexample\n"
  92. "@ingroup gameObjects\n"
  93. );
  94. ItemData::ItemData()
  95. {
  96. shadowEnable = true;
  97. friction = 0;
  98. elasticity = 0;
  99. sticky = false;
  100. gravityMod = 1.0;
  101. maxVelocity = 25.0f;
  102. density = 2;
  103. drag = 0.5;
  104. lightOnlyStatic = false;
  105. lightType = Item::NoLight;
  106. lightColor.set(1.f,1.f,1.f,1.f);
  107. lightTime = 1000;
  108. lightRadius = 10.f;
  109. simpleServerCollision = true;
  110. }
  111. ImplementEnumType( ItemLightType,
  112. "@brief The type of light the Item has\n\n"
  113. "@ingroup gameObjects\n\n")
  114. { Item::NoLight, "NoLight", "The item has no light attached.\n" },
  115. { Item::ConstantLight, "ConstantLight", "The item has a constantly emitting light attached.\n" },
  116. { Item::PulsingLight, "PulsingLight", "The item has a pulsing light attached.\n" }
  117. EndImplementEnumType;
  118. void ItemData::initPersistFields()
  119. {
  120. addField("friction", TypeF32, Offset(friction, ItemData), "A floating-point value specifying how much velocity is lost to impact and sliding friction.");
  121. addField("elasticity", TypeF32, Offset(elasticity, ItemData), "A floating-point value specifying how 'bouncy' this ItemData is.");
  122. addField("sticky", TypeBool, Offset(sticky, ItemData),
  123. "@brief If true, ItemData will 'stick' to any surface it collides with.\n\n"
  124. "When an item does stick to a surface, the Item::onStickyCollision() callback is called. The Item has methods to retrieve "
  125. "the world position and normal the Item is stuck to.\n"
  126. "@note Valid objects to stick to must be of StaticShapeObjectType.\n");
  127. addField("gravityMod", TypeF32, Offset(gravityMod, ItemData), "Floating point value to multiply the existing gravity with, just for this ItemData.");
  128. addField("maxVelocity", TypeF32, Offset(maxVelocity, ItemData), "Maximum velocity that this ItemData is able to move.");
  129. addField("lightType", TYPEID< Item::LightType >(), Offset(lightType, ItemData), "Type of light to apply to this ItemData. Options are NoLight, ConstantLight, PulsingLight. Default is NoLight." );
  130. addField("lightColor", TypeColorF, Offset(lightColor, ItemData),
  131. "@brief Color value to make this light. Example: \"1.0,1.0,1.0\"\n\n"
  132. "@see lightType\n");
  133. addField("lightTime", TypeS32, Offset(lightTime, ItemData),
  134. "@brief Time value for the light of this ItemData, used to control the pulse speed of the PulsingLight LightType.\n\n"
  135. "@see lightType\n");
  136. addField("lightRadius", TypeF32, Offset(lightRadius, ItemData),
  137. "@brief Distance from the center point of this ItemData for the light to affect\n\n"
  138. "@see lightType\n");
  139. addField("lightOnlyStatic", TypeBool, Offset(lightOnlyStatic, ItemData),
  140. "@brief If true, this ItemData will only cast a light if the Item for this ItemData has a static value of true.\n\n"
  141. "@see lightType\n");
  142. addField("simpleServerCollision", TypeBool, Offset(simpleServerCollision, ItemData),
  143. "@brief Determines if only simple server-side collision will be used (for pick ups).\n\n"
  144. "If set to true then only simple, server-side collision detection will be used. This is often the case "
  145. "if the item is used for a pick up object, such as ammo. If set to false then a full collision volume "
  146. "will be used as defined by the shape. The default is true.\n"
  147. "@note Only applies when using a physics library.\n"
  148. "@see TurretShape and ProximityMine for examples that should set this to false to allow them to be "
  149. "shot by projectiles.\n");
  150. Parent::initPersistFields();
  151. }
  152. void ItemData::packData(BitStream* stream)
  153. {
  154. Parent::packData(stream);
  155. stream->writeFloat(friction, 10);
  156. stream->writeFloat(elasticity, 10);
  157. stream->writeFlag(sticky);
  158. if(stream->writeFlag(gravityMod != 1.0))
  159. stream->writeFloat(gravityMod, 10);
  160. if(stream->writeFlag(maxVelocity != -1))
  161. stream->write(maxVelocity);
  162. if(stream->writeFlag(lightType != Item::NoLight))
  163. {
  164. AssertFatal(Item::NumLightTypes < (1 << 2), "ItemData: light type needs more bits");
  165. stream->writeInt(lightType, 2);
  166. stream->writeFloat(lightColor.red, 7);
  167. stream->writeFloat(lightColor.green, 7);
  168. stream->writeFloat(lightColor.blue, 7);
  169. stream->writeFloat(lightColor.alpha, 7);
  170. stream->write(lightTime);
  171. stream->write(lightRadius);
  172. stream->writeFlag(lightOnlyStatic);
  173. }
  174. stream->writeFlag(simpleServerCollision);
  175. }
  176. void ItemData::unpackData(BitStream* stream)
  177. {
  178. Parent::unpackData(stream);
  179. friction = stream->readFloat(10);
  180. elasticity = stream->readFloat(10);
  181. sticky = stream->readFlag();
  182. if(stream->readFlag())
  183. gravityMod = stream->readFloat(10);
  184. else
  185. gravityMod = 1.0;
  186. if(stream->readFlag())
  187. stream->read(&maxVelocity);
  188. else
  189. maxVelocity = -1;
  190. if(stream->readFlag())
  191. {
  192. lightType = stream->readInt(2);
  193. lightColor.red = stream->readFloat(7);
  194. lightColor.green = stream->readFloat(7);
  195. lightColor.blue = stream->readFloat(7);
  196. lightColor.alpha = stream->readFloat(7);
  197. stream->read(&lightTime);
  198. stream->read(&lightRadius);
  199. lightOnlyStatic = stream->readFlag();
  200. }
  201. else
  202. lightType = Item::NoLight;
  203. simpleServerCollision = stream->readFlag();
  204. }
  205. //----------------------------------------------------------------------------
  206. IMPLEMENT_CO_NETOBJECT_V1(Item);
  207. ConsoleDocClass( Item,
  208. "@brief Base Item class. Uses the ItemData datablock for common properties.\n\n"
  209. "Items represent an object in the world, usually one that the player will interact with. "
  210. "One example is a health kit on the group that is automatically picked up when the player "
  211. "comes into contact with it.\n\n"
  212. "@tsexample\n"
  213. "// This is the \"health patch\" dropped by a dying player.\n"
  214. "datablock ItemData(HealthKitPatch)\n"
  215. "{\n"
  216. " // Mission editor category, this datablock will show up in the\n"
  217. " // specified category under the \"shapes\" root category.\n"
  218. " category = \"Health\";\n\n"
  219. " className = \"HealthPatch\";\n\n"
  220. " // Basic Item properties\n"
  221. " shapeFile = \"art/shapes/items/patch/healthpatch.dts\";\n"
  222. " mass = 2;\n"
  223. " friction = 1;\n"
  224. " elasticity = 0.3;\n"
  225. " emap = true;\n\n"
  226. " // Dynamic properties used by the scripts\n"
  227. " pickupName = \"a health patch\";\n"
  228. " repairAmount = 50;\n"
  229. "};\n\n"
  230. "%obj = new Item()\n"
  231. "{\n"
  232. " dataBlock = HealthKitSmall;\n"
  233. " parentGroup = EWCreatorWindow.objectGroup;\n"
  234. " static = true;\n"
  235. " rotate = true;\n"
  236. "};\n"
  237. "@endtsexample\n\n"
  238. "@see ItemData\n"
  239. "@ingroup gameObjects\n"
  240. );
  241. IMPLEMENT_CALLBACK( Item, onStickyCollision, void, ( const char* objID ),( objID ),
  242. "@brief Informs the Item object that it is now sticking to another object.\n\n"
  243. "This callback is only called if the ItemData::sticky property for this Item is true.\n"
  244. "@param objID Object ID this Item object.\n"
  245. "@note Server side only.\n"
  246. "@see Item, ItemData\n"
  247. );
  248. IMPLEMENT_CALLBACK( Item, onEnterLiquid, void, ( const char* objID, F32 waterCoverage, const char* liquidType ),( objID, waterCoverage, liquidType ),
  249. "Informs an Item object that it has entered liquid, along with information about the liquid type.\n"
  250. "@param objID Object ID for this Item object.\n"
  251. "@param waterCoverage How much coverage of water this Item object has.\n"
  252. "@param liquidType The type of liquid that this Item object has entered.\n"
  253. "@note Server side only.\n"
  254. "@see Item, ItemData, WaterObject\n"
  255. );
  256. IMPLEMENT_CALLBACK( Item, onLeaveLiquid, void, ( const char* objID, const char* liquidType ),( objID, liquidType ),
  257. "Informs an Item object that it has left a liquid, along with information about the liquid type.\n"
  258. "@param objID Object ID for this Item object.\n"
  259. "@param liquidType The type of liquid that this Item object has left.\n"
  260. "@note Server side only.\n"
  261. "@see Item, ItemData, WaterObject\n"
  262. );
  263. Item::Item()
  264. {
  265. mTypeMask |= ItemObjectType | DynamicShapeObjectType;
  266. mDataBlock = 0;
  267. mStatic = false;
  268. mRotate = false;
  269. mVelocity = VectorF(0,0,0);
  270. mAtRest = true;
  271. mAtRestCounter = 0;
  272. mInLiquid = false;
  273. delta.warpTicks = 0;
  274. delta.dt = 1;
  275. mCollisionObject = 0;
  276. mCollisionTimeout = 0;
  277. mPhysicsRep = NULL;
  278. mConvex.init(this);
  279. mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
  280. mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
  281. mLight = NULL;
  282. mSubclassItemHandlesScene = false;
  283. }
  284. Item::~Item()
  285. {
  286. SAFE_DELETE(mLight);
  287. }
  288. //----------------------------------------------------------------------------
  289. bool Item::onAdd()
  290. {
  291. if (!Parent::onAdd() || !mDataBlock)
  292. return false;
  293. if (mStatic)
  294. mAtRest = true;
  295. mObjToWorld.getColumn(3,&delta.pos);
  296. // Setup the box for our convex object...
  297. mObjBox.getCenter(&mConvex.mCenter);
  298. mConvex.mSize.x = mObjBox.len_x() / 2.0;
  299. mConvex.mSize.y = mObjBox.len_y() / 2.0;
  300. mConvex.mSize.z = mObjBox.len_z() / 2.0;
  301. mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
  302. mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
  303. if( !isHidden() && !mSubclassItemHandlesScene )
  304. addToScene();
  305. if (isServerObject())
  306. {
  307. if (!mSubclassItemHandlesScene)
  308. scriptOnAdd();
  309. }
  310. else if (mDataBlock->lightType != NoLight)
  311. {
  312. mDropTime = Sim::getCurrentTime();
  313. }
  314. _updatePhysics();
  315. return true;
  316. }
  317. void Item::_updatePhysics()
  318. {
  319. SAFE_DELETE( mPhysicsRep );
  320. if ( !PHYSICSMGR )
  321. return;
  322. if (mDataBlock->simpleServerCollision)
  323. {
  324. // We only need the trigger on the server.
  325. if ( isServerObject() )
  326. {
  327. PhysicsCollision *colShape = PHYSICSMGR->createCollision();
  328. colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
  329. PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
  330. mPhysicsRep = PHYSICSMGR->createBody();
  331. mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
  332. mPhysicsRep->setTransform( getTransform() );
  333. }
  334. }
  335. else
  336. {
  337. if ( !mShapeInstance )
  338. return;
  339. PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
  340. if ( colShape )
  341. {
  342. PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
  343. mPhysicsRep = PHYSICSMGR->createBody();
  344. mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
  345. mPhysicsRep->setTransform( getTransform() );
  346. }
  347. }
  348. }
  349. bool Item::onNewDataBlock( GameBaseData *dptr, bool reload )
  350. {
  351. mDataBlock = dynamic_cast<ItemData*>(dptr);
  352. if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
  353. return false;
  354. if (!mSubclassItemHandlesScene)
  355. scriptOnNewDataBlock();
  356. if ( isProperlyAdded() )
  357. _updatePhysics();
  358. return true;
  359. }
  360. void Item::onRemove()
  361. {
  362. mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
  363. mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
  364. SAFE_DELETE( mPhysicsRep );
  365. if (!mSubclassItemHandlesScene)
  366. {
  367. scriptOnRemove();
  368. removeFromScene();
  369. }
  370. Parent::onRemove();
  371. }
  372. void Item::onDeleteNotify( SimObject *obj )
  373. {
  374. if ( obj == mCollisionObject )
  375. {
  376. mCollisionObject = NULL;
  377. mCollisionTimeout = 0;
  378. }
  379. Parent::onDeleteNotify( obj );
  380. }
  381. // Lighting: -----------------------------------------------------------------
  382. void Item::registerLights(LightManager * lightManager, bool lightingScene)
  383. {
  384. if(lightingScene)
  385. return;
  386. if(mDataBlock->lightOnlyStatic && !mStatic)
  387. return;
  388. F32 intensity;
  389. switch(mDataBlock->lightType)
  390. {
  391. case ConstantLight:
  392. intensity = mFadeVal;
  393. break;
  394. case PulsingLight:
  395. {
  396. S32 delta = Sim::getCurrentTime() - mDropTime;
  397. intensity = 0.5f + 0.5f * mSin(M_PI_F * F32(delta) / F32(mDataBlock->lightTime));
  398. intensity = 0.15f + intensity * 0.85f;
  399. intensity *= mFadeVal; // fade out light on flags
  400. break;
  401. }
  402. default:
  403. return;
  404. }
  405. // Create a light if needed
  406. if (!mLight)
  407. {
  408. mLight = lightManager->createLightInfo();
  409. }
  410. mLight->setColor( mDataBlock->lightColor * intensity );
  411. mLight->setType( LightInfo::Point );
  412. mLight->setRange( mDataBlock->lightRadius );
  413. mLight->setPosition( getBoxCenter() );
  414. lightManager->registerGlobalLight( mLight, this );
  415. }
  416. //----------------------------------------------------------------------------
  417. Point3F Item::getVelocity() const
  418. {
  419. return mVelocity;
  420. }
  421. void Item::setVelocity(const VectorF& vel)
  422. {
  423. mVelocity = vel;
  424. // Clamp against the maximum velocity.
  425. if ( mDataBlock->maxVelocity > 0 )
  426. {
  427. F32 len = mVelocity.magnitudeSafe();
  428. if ( len > mDataBlock->maxVelocity )
  429. {
  430. Point3F excess = mVelocity * ( 1.0f - (mDataBlock->maxVelocity / len ) );
  431. mVelocity -= excess;
  432. }
  433. }
  434. setMaskBits(PositionMask);
  435. mAtRest = false;
  436. mAtRestCounter = 0;
  437. }
  438. void Item::applyImpulse(const Point3F&,const VectorF& vec)
  439. {
  440. // Items ignore angular velocity
  441. VectorF vel;
  442. vel.x = vec.x / mDataBlock->mass;
  443. vel.y = vec.y / mDataBlock->mass;
  444. vel.z = vec.z / mDataBlock->mass;
  445. setVelocity(vel);
  446. }
  447. void Item::setCollisionTimeout(ShapeBase* obj)
  448. {
  449. if (mCollisionObject)
  450. clearNotify(mCollisionObject);
  451. deleteNotify(obj);
  452. mCollisionObject = obj;
  453. mCollisionTimeout = sCollisionTimeout;
  454. setMaskBits(ThrowSrcMask);
  455. }
  456. //----------------------------------------------------------------------------
  457. void Item::processTick(const Move* move)
  458. {
  459. Parent::processTick(move);
  460. if ( isMounted() )
  461. return;
  462. //
  463. if (mCollisionObject && !--mCollisionTimeout)
  464. mCollisionObject = 0;
  465. // Warp to catch up to server
  466. if (delta.warpTicks > 0)
  467. {
  468. delta.warpTicks--;
  469. // Set new pos.
  470. MatrixF mat = mObjToWorld;
  471. mat.getColumn(3,&delta.pos);
  472. delta.pos += delta.warpOffset;
  473. mat.setColumn(3,delta.pos);
  474. Parent::setTransform(mat);
  475. // Backstepping
  476. delta.posVec.x = -delta.warpOffset.x;
  477. delta.posVec.y = -delta.warpOffset.y;
  478. delta.posVec.z = -delta.warpOffset.z;
  479. }
  480. else
  481. {
  482. if (isServerObject() && mAtRest && (mStatic == false && mDataBlock->sticky == false))
  483. {
  484. if (++mAtRestCounter > csmAtRestTimer)
  485. {
  486. mAtRest = false;
  487. mAtRestCounter = 0;
  488. setMaskBits(PositionMask);
  489. }
  490. }
  491. if (!mStatic && !mAtRest && isHidden() == false)
  492. {
  493. updateVelocity(TickSec);
  494. updateWorkingCollisionSet(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
  495. updatePos(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
  496. }
  497. else
  498. {
  499. // Need to clear out last updatePos or warp interpolation
  500. delta.posVec.set(0,0,0);
  501. }
  502. }
  503. }
  504. void Item::interpolateTick(F32 dt)
  505. {
  506. Parent::interpolateTick(dt);
  507. if ( isMounted() )
  508. return;
  509. // Client side interpolation
  510. Point3F pos = delta.pos + delta.posVec * dt;
  511. MatrixF mat = mRenderObjToWorld;
  512. mat.setColumn(3,pos);
  513. setRenderTransform(mat);
  514. delta.dt = dt;
  515. }
  516. //----------------------------------------------------------------------------
  517. void Item::setTransform(const MatrixF& mat)
  518. {
  519. Point3F pos;
  520. mat.getColumn(3,&pos);
  521. MatrixF tmat;
  522. if (!mRotate) {
  523. // Forces all rotation to be around the z axis
  524. VectorF vec;
  525. mat.getColumn(1,&vec);
  526. tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y)));
  527. }
  528. else
  529. tmat.identity();
  530. tmat.setColumn(3,pos);
  531. Parent::setTransform(tmat);
  532. if (!mStatic)
  533. {
  534. mAtRest = false;
  535. mAtRestCounter = 0;
  536. }
  537. if ( mPhysicsRep )
  538. mPhysicsRep->setTransform( getTransform() );
  539. setMaskBits(RotationMask | PositionMask | NoWarpMask);
  540. }
  541. //----------------------------------------------------------------------------
  542. void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt)
  543. {
  544. // It is assumed that we will never accelerate more than 10 m/s for gravity...
  545. //
  546. Point3F scaledVelocity = mVelocity * dt;
  547. F32 len = scaledVelocity.len();
  548. F32 newLen = len + (10 * dt);
  549. // Check to see if it is actually necessary to construct the new working list,
  550. // or if we can use the cached version from the last query. We use the x
  551. // component of the min member of the mWorkingQueryBox, which is lame, but
  552. // it works ok.
  553. bool updateSet = false;
  554. Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
  555. F32 l = (newLen * 1.1) + 0.1; // from Convex::updateWorkingList
  556. convexBox.minExtents -= Point3F(l, l, l);
  557. convexBox.maxExtents += Point3F(l, l, l);
  558. // Check containment
  559. {
  560. if (mWorkingQueryBox.minExtents.x != -1e9)
  561. {
  562. if (mWorkingQueryBox.isContained(convexBox) == false)
  563. {
  564. // Needed region is outside the cached region. Update it.
  565. updateSet = true;
  566. }
  567. else
  568. {
  569. // We can leave it alone, we're still inside the cached region
  570. }
  571. }
  572. else
  573. {
  574. // Must update
  575. updateSet = true;
  576. }
  577. }
  578. // Actually perform the query, if necessary
  579. if (updateSet == true)
  580. {
  581. mWorkingQueryBox = convexBox;
  582. mWorkingQueryBox.minExtents -= Point3F(2 * l, 2 * l, 2 * l);
  583. mWorkingQueryBox.maxExtents += Point3F(2 * l, 2 * l, 2 * l);
  584. disableCollision();
  585. if (mCollisionObject)
  586. mCollisionObject->disableCollision();
  587. mConvex.updateWorkingList(mWorkingQueryBox, mask);
  588. if (mCollisionObject)
  589. mCollisionObject->enableCollision();
  590. enableCollision();
  591. }
  592. }
  593. void Item::updateVelocity(const F32 dt)
  594. {
  595. // Acceleration due to gravity
  596. mVelocity.z += (mGravity * mDataBlock->gravityMod) * dt;
  597. F32 len;
  598. if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) {
  599. Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len ));
  600. excess *= 0.1f;
  601. mVelocity -= excess;
  602. }
  603. // Container buoyancy & drag
  604. mVelocity.z -= mBuoyancy * (mGravity * mDataBlock->gravityMod * mGravityMod) * dt;
  605. mVelocity -= mVelocity * mDrag * dt;
  606. }
  607. void Item::updatePos(const U32 /*mask*/, const F32 dt)
  608. {
  609. // Try and move
  610. Point3F pos;
  611. mObjToWorld.getColumn(3,&pos);
  612. delta.posVec = pos;
  613. bool contact = false;
  614. bool nonStatic = false;
  615. bool stickyNotify = false;
  616. CollisionList collisionList;
  617. F32 time = dt;
  618. static Polyhedron sBoxPolyhedron;
  619. static ExtrudedPolyList sExtrudedPolyList;
  620. static EarlyOutPolyList sEarlyOutPolyList;
  621. MatrixF collisionMatrix(true);
  622. Point3F end = pos + mVelocity * time;
  623. U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask;
  624. // Part of our speed problem here is that we don't track contact surfaces, like we do
  625. // with the player. In order to handle the most common and performance impacting
  626. // instance of this problem, we'll use a ray cast to detect any contact surfaces below
  627. // us. This won't be perfect, but it only needs to catch a few of these to make a
  628. // big difference. We'll cast from the top center of the bounding box at the tick's
  629. // beginning to the bottom center of the box at the end.
  630. Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  631. (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
  632. mObjBox.maxExtents.z);
  633. Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  634. (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
  635. mObjBox.minExtents.z);
  636. collisionMatrix.setColumn(3, pos);
  637. collisionMatrix.mulP(startCast);
  638. collisionMatrix.setColumn(3, end);
  639. collisionMatrix.mulP(endCast);
  640. RayInfo rinfo;
  641. bool doToughCollision = true;
  642. disableCollision();
  643. if (mCollisionObject)
  644. mCollisionObject->disableCollision();
  645. if (getContainer()->castRay(startCast, endCast, mask, &rinfo))
  646. {
  647. F32 bd = -mDot(mVelocity, rinfo.normal);
  648. if (bd >= 0.0)
  649. {
  650. // Contact!
  651. if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
  652. mVelocity.set(0, 0, 0);
  653. mAtRest = true;
  654. mAtRestCounter = 0;
  655. stickyNotify = true;
  656. mStickyCollisionPos = rinfo.point;
  657. mStickyCollisionNormal = rinfo.normal;
  658. doToughCollision = false;;
  659. } else {
  660. // Subtract out velocity into surface and friction
  661. VectorF fv = mVelocity + rinfo.normal * bd;
  662. F32 fvl = fv.len();
  663. if (fvl) {
  664. F32 ff = bd * mDataBlock->friction;
  665. if (ff < fvl) {
  666. fv *= ff / fvl;
  667. fvl = ff;
  668. }
  669. }
  670. bd *= 1 + mDataBlock->elasticity;
  671. VectorF dv = rinfo.normal * (bd + 0.002);
  672. mVelocity += dv;
  673. mVelocity -= fv;
  674. // Keep track of what we hit
  675. contact = true;
  676. U32 typeMask = rinfo.object->getTypeMask();
  677. if (!(typeMask & StaticObjectType))
  678. nonStatic = true;
  679. if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
  680. ShapeBase* col = static_cast<ShapeBase*>(rinfo.object);
  681. queueCollision(col,mVelocity - col->getVelocity());
  682. }
  683. }
  684. }
  685. }
  686. enableCollision();
  687. if (mCollisionObject)
  688. mCollisionObject->enableCollision();
  689. if (doToughCollision)
  690. {
  691. U32 count;
  692. for (count = 0; count < 3; count++)
  693. {
  694. // Build list from convex states here...
  695. end = pos + mVelocity * time;
  696. collisionMatrix.setColumn(3, end);
  697. Box3F wBox = getObjBox();
  698. collisionMatrix.mul(wBox);
  699. Box3F testBox = wBox;
  700. Point3F oldMin = testBox.minExtents;
  701. Point3F oldMax = testBox.maxExtents;
  702. testBox.minExtents.setMin(oldMin + (mVelocity * time));
  703. testBox.maxExtents.setMin(oldMax + (mVelocity * time));
  704. sEarlyOutPolyList.clear();
  705. sEarlyOutPolyList.mNormal.set(0,0,0);
  706. sEarlyOutPolyList.mPlaneList.setSize(6);
  707. sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0));
  708. sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0));
  709. sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0));
  710. sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0));
  711. sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1));
  712. sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1));
  713. CollisionWorkingList& eorList = mConvex.getWorkingList();
  714. CollisionWorkingList* eopList = eorList.wLink.mNext;
  715. while (eopList != &eorList) {
  716. if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0)
  717. {
  718. Box3F convexBox = eopList->mConvex->getBoundingBox();
  719. if (testBox.isOverlapped(convexBox))
  720. {
  721. eopList->mConvex->getPolyList(&sEarlyOutPolyList);
  722. if (sEarlyOutPolyList.isEmpty() == false)
  723. break;
  724. }
  725. }
  726. eopList = eopList->wLink.mNext;
  727. }
  728. if (sEarlyOutPolyList.isEmpty())
  729. {
  730. pos = end;
  731. break;
  732. }
  733. collisionMatrix.setColumn(3, pos);
  734. sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true);
  735. // Build extruded polyList...
  736. VectorF vector = end - pos;
  737. sExtrudedPolyList.extrude(sBoxPolyhedron, vector);
  738. sExtrudedPolyList.setVelocity(mVelocity);
  739. sExtrudedPolyList.setCollisionList(&collisionList);
  740. CollisionWorkingList& rList = mConvex.getWorkingList();
  741. CollisionWorkingList* pList = rList.wLink.mNext;
  742. while (pList != &rList) {
  743. if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0)
  744. {
  745. Box3F convexBox = pList->mConvex->getBoundingBox();
  746. if (testBox.isOverlapped(convexBox))
  747. {
  748. pList->mConvex->getPolyList(&sExtrudedPolyList);
  749. }
  750. }
  751. pList = pList->wLink.mNext;
  752. }
  753. if (collisionList.getTime() < 1.0)
  754. {
  755. // Set to collision point
  756. F32 dt = time * collisionList.getTime();
  757. pos += mVelocity * dt;
  758. time -= dt;
  759. // Pick the most resistant surface
  760. F32 bd = 0;
  761. const Collision* collision = 0;
  762. for (S32 c = 0; c < collisionList.getCount(); c++) {
  763. const Collision &cp = collisionList[c];
  764. F32 dot = -mDot(mVelocity,cp.normal);
  765. if (dot > bd) {
  766. bd = dot;
  767. collision = &cp;
  768. }
  769. }
  770. if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
  771. mVelocity.set(0, 0, 0);
  772. mAtRest = true;
  773. mAtRestCounter = 0;
  774. stickyNotify = true;
  775. mStickyCollisionPos = collision->point;
  776. mStickyCollisionNormal = collision->normal;
  777. break;
  778. } else {
  779. // Subtract out velocity into surface and friction
  780. if (collision) {
  781. VectorF fv = mVelocity + collision->normal * bd;
  782. F32 fvl = fv.len();
  783. if (fvl) {
  784. F32 ff = bd * mDataBlock->friction;
  785. if (ff < fvl) {
  786. fv *= ff / fvl;
  787. fvl = ff;
  788. }
  789. }
  790. bd *= 1 + mDataBlock->elasticity;
  791. VectorF dv = collision->normal * (bd + 0.002);
  792. mVelocity += dv;
  793. mVelocity -= fv;
  794. // Keep track of what we hit
  795. contact = true;
  796. U32 typeMask = collision->object->getTypeMask();
  797. if (!(typeMask & StaticObjectType))
  798. nonStatic = true;
  799. if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
  800. ShapeBase* col = static_cast<ShapeBase*>(collision->object);
  801. queueCollision(col,mVelocity - col->getVelocity());
  802. }
  803. }
  804. }
  805. }
  806. else
  807. {
  808. pos = end;
  809. break;
  810. }
  811. }
  812. if (count == 3)
  813. {
  814. // Couldn't move...
  815. mVelocity.set(0, 0, 0);
  816. }
  817. }
  818. // If on the client, calculate delta for backstepping
  819. if (isGhost()) {
  820. delta.pos = pos;
  821. delta.posVec -= pos;
  822. delta.dt = 1;
  823. }
  824. // Update transform
  825. MatrixF mat = mObjToWorld;
  826. mat.setColumn(3,pos);
  827. Parent::setTransform(mat);
  828. enableCollision();
  829. if (mCollisionObject)
  830. mCollisionObject->enableCollision();
  831. updateContainer();
  832. if ( mPhysicsRep )
  833. mPhysicsRep->setTransform( mat );
  834. //
  835. if (contact) {
  836. // Check for rest condition
  837. if (!nonStatic && mVelocity.len() < sAtRestVelocity) {
  838. mVelocity.x = mVelocity.y = mVelocity.z = 0;
  839. mAtRest = true;
  840. mAtRestCounter = 0;
  841. }
  842. // Only update the client if we hit a non-static shape or
  843. // if this is our final rest pos.
  844. if (nonStatic || mAtRest)
  845. setMaskBits(PositionMask);
  846. }
  847. // Collision callbacks. These need to be processed whether we hit
  848. // anything or not.
  849. if (!isGhost())
  850. {
  851. SimObjectPtr<Item> safePtr(this);
  852. if (stickyNotify)
  853. {
  854. notifyCollision();
  855. if(bool(safePtr))
  856. onStickyCollision_callback( getIdString() );
  857. }
  858. else
  859. notifyCollision();
  860. // water
  861. if(bool(safePtr))
  862. {
  863. if(!mInLiquid && mWaterCoverage != 0.0f)
  864. {
  865. onEnterLiquid_callback( getIdString(), mWaterCoverage, mLiquidType.c_str() );
  866. mInLiquid = true;
  867. }
  868. else if(mInLiquid && mWaterCoverage == 0.0f)
  869. {
  870. onLeaveLiquid_callback(getIdString(), mLiquidType.c_str());
  871. mInLiquid = false;
  872. }
  873. }
  874. }
  875. }
  876. //----------------------------------------------------------------------------
  877. static MatrixF IMat(1);
  878. bool Item::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF&)
  879. {
  880. if ( context == PLC_Decal )
  881. return false;
  882. // Collision with the item is always against the item's object
  883. // space bounding box axis aligned in world space.
  884. Point3F pos;
  885. mObjToWorld.getColumn(3,&pos);
  886. IMat.setColumn(3,pos);
  887. polyList->setTransform(&IMat, mObjScale);
  888. polyList->setObject(this);
  889. polyList->addBox(mObjBox);
  890. return true;
  891. }
  892. //----------------------------------------------------------------------------
  893. U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
  894. {
  895. U32 retMask = Parent::packUpdate(connection,mask,stream);
  896. if (stream->writeFlag(mask & InitialUpdateMask)) {
  897. stream->writeFlag(mRotate);
  898. stream->writeFlag(mStatic);
  899. if (stream->writeFlag(getScale() != Point3F(1, 1, 1)))
  900. mathWrite(*stream, getScale());
  901. }
  902. if (mask & ThrowSrcMask && mCollisionObject) {
  903. S32 gIndex = connection->getGhostIndex(mCollisionObject);
  904. if (stream->writeFlag(gIndex != -1))
  905. stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
  906. }
  907. else
  908. stream->writeFlag(false);
  909. if (stream->writeFlag(mask & RotationMask && !mRotate)) {
  910. // Assumes rotation is about the Z axis
  911. AngAxisF aa(mObjToWorld);
  912. stream->writeFlag(aa.axis.z < 0);
  913. stream->write(aa.angle);
  914. }
  915. if (stream->writeFlag(mask & PositionMask)) {
  916. Point3F pos;
  917. mObjToWorld.getColumn(3,&pos);
  918. mathWrite(*stream, pos);
  919. if (!stream->writeFlag(mAtRest)) {
  920. mathWrite(*stream, mVelocity);
  921. }
  922. stream->writeFlag(!(mask & NoWarpMask));
  923. }
  924. return retMask;
  925. }
  926. void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
  927. {
  928. Parent::unpackUpdate(connection,stream);
  929. // InitialUpdateMask
  930. if (stream->readFlag()) {
  931. mRotate = stream->readFlag();
  932. mStatic = stream->readFlag();
  933. if (stream->readFlag())
  934. mathRead(*stream, &mObjScale);
  935. else
  936. mObjScale.set(1, 1, 1);
  937. }
  938. // ThrowSrcMask && mCollisionObject
  939. if (stream->readFlag()) {
  940. S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
  941. setCollisionTimeout(static_cast<ShapeBase*>(connection->resolveGhost(gIndex)));
  942. }
  943. MatrixF mat = mObjToWorld;
  944. // RotationMask && !mRotate
  945. if (stream->readFlag()) {
  946. // Assumes rotation is about the Z axis
  947. AngAxisF aa;
  948. aa.axis.set(0.0f, 0.0f, stream->readFlag() ? -1.0f : 1.0f);
  949. stream->read(&aa.angle);
  950. aa.setMatrix(&mat);
  951. Point3F pos;
  952. mObjToWorld.getColumn(3,&pos);
  953. mat.setColumn(3,pos);
  954. }
  955. // PositionMask
  956. if (stream->readFlag()) {
  957. Point3F pos;
  958. mathRead(*stream, &pos);
  959. F32 speed = mVelocity.len();
  960. if ((mAtRest = stream->readFlag()) == true)
  961. mVelocity.set(0.0f, 0.0f, 0.0f);
  962. else
  963. mathRead(*stream, &mVelocity);
  964. if (stream->readFlag() && isProperlyAdded()) {
  965. // Determin number of ticks to warp based on the average
  966. // of the client and server velocities.
  967. delta.warpOffset = pos - delta.pos;
  968. F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
  969. F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
  970. delta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5f), 1.0f): 0.0f);
  971. if (delta.warpTicks)
  972. {
  973. // Setup the warp to start on the next tick, only the
  974. // object's position is warped.
  975. if (delta.warpTicks > sMaxWarpTicks)
  976. delta.warpTicks = sMaxWarpTicks;
  977. delta.warpOffset /= (F32)delta.warpTicks;
  978. }
  979. else {
  980. // Going to skip the warp, server and client are real close.
  981. // Adjust the frame interpolation to move smoothly to the
  982. // new position within the current tick.
  983. Point3F cp = delta.pos + delta.posVec * delta.dt;
  984. VectorF vec = delta.pos - cp;
  985. F32 vl = vec.len();
  986. if (vl) {
  987. F32 s = delta.posVec.len() / vl;
  988. delta.posVec = (cp - pos) * s;
  989. }
  990. delta.pos = pos;
  991. mat.setColumn(3,pos);
  992. }
  993. }
  994. else {
  995. // Set the item to the server position
  996. delta.warpTicks = 0;
  997. delta.posVec.set(0,0,0);
  998. delta.pos = pos;
  999. delta.dt = 0;
  1000. mat.setColumn(3,pos);
  1001. }
  1002. }
  1003. Parent::setTransform(mat);
  1004. }
  1005. DefineEngineMethod( Item, isStatic, bool, (),,
  1006. "@brief Is the object static (ie, non-movable)?\n\n"
  1007. "@return True if the object is static, false if it is not.\n"
  1008. "@tsexample\n"
  1009. "// Query the item on if it is or is not static.\n"
  1010. "%isStatic = %itemData.isStatic();\n\n"
  1011. "@endtsexample\n\n"
  1012. "@see static\n"
  1013. )
  1014. {
  1015. return object->isStatic();
  1016. }
  1017. DefineEngineMethod( Item, isAtRest, bool, (),,
  1018. "@brief Is the object at rest (ie, no longer moving)?\n\n"
  1019. "@return True if the object is at rest, false if it is not.\n"
  1020. "@tsexample\n"
  1021. "// Query the item on if it is or is not at rest.\n"
  1022. "%isAtRest = %item.isAtRest();\n\n"
  1023. "@endtsexample\n\n"
  1024. )
  1025. {
  1026. return object->isAtRest();
  1027. }
  1028. DefineEngineMethod( Item, isRotating, bool, (),,
  1029. "@brief Is the object still rotating?\n\n"
  1030. "@return True if the object is still rotating, false if it is not.\n"
  1031. "@tsexample\n"
  1032. "// Query the item on if it is or is not rotating.\n"
  1033. "%isRotating = %itemData.isRotating();\n\n"
  1034. "@endtsexample\n\n"
  1035. "@see rotate\n"
  1036. )
  1037. {
  1038. return object->isRotating();
  1039. }
  1040. DefineEngineMethod( Item, setCollisionTimeout, bool, (S32 ignoreColObj),(NULL),
  1041. "@brief Temporarily disable collisions against a specific ShapeBase object.\n\n"
  1042. "This is useful to prevent a player from immediately picking up an Item they have "
  1043. "just thrown. Only one object may be on the timeout list at a time. The timeout is "
  1044. "defined as 15 ticks.\n\n"
  1045. "@param objectID ShapeBase object ID to disable collisions against.\n"
  1046. "@return Returns true if the ShapeBase object requested could be found, false if it could not.\n"
  1047. "@tsexample\n"
  1048. "// Set the ShapeBase Object ID to disable collisions against\n"
  1049. "%ignoreColObj = %player.getID();\n\n"
  1050. "// Inform this Item object to ignore collisions temproarily against the %ignoreColObj.\n"
  1051. "%item.setCollisionTimeout(%ignoreColObj);\n\n"
  1052. "@endtsexample\n\n"
  1053. )
  1054. {
  1055. ShapeBase* source = NULL;
  1056. if (Sim::findObject(ignoreColObj,source)) {
  1057. object->setCollisionTimeout(source);
  1058. return true;
  1059. }
  1060. return false;
  1061. }
  1062. DefineEngineMethod( Item, getLastStickyPos, const char*, (),,
  1063. "@brief Get the position on the surface on which this Item is stuck.\n\n"
  1064. "@return Returns The XYZ position of where this Item is stuck.\n"
  1065. "@tsexample\n"
  1066. "// Acquire the position where this Item is currently stuck\n"
  1067. "%stuckPosition = %item.getLastStickPos();\n\n"
  1068. "@endtsexample\n\n"
  1069. "@note Server side only.\n"
  1070. )
  1071. {
  1072. static const U32 bufSize = 256;
  1073. char* ret = Con::getReturnBuffer(bufSize);
  1074. if (object->isServerObject())
  1075. dSprintf(ret, bufSize, "%g %g %g",
  1076. object->mStickyCollisionPos.x,
  1077. object->mStickyCollisionPos.y,
  1078. object->mStickyCollisionPos.z);
  1079. else
  1080. dStrcpy(ret, "0 0 0");
  1081. return ret;
  1082. }
  1083. DefineEngineMethod( Item, getLastStickyNormal, const char *, (),,
  1084. "@brief Get the normal of the surface on which the object is stuck.\n\n"
  1085. "@return Returns The XYZ normal from where this Item is stuck.\n"
  1086. "@tsexample\n"
  1087. "// Acquire the position where this Item is currently stuck\n"
  1088. "%stuckPosition = %item.getLastStickPos();\n\n"
  1089. "@endtsexample\n\n"
  1090. "@note Server side only.\n"
  1091. )
  1092. {
  1093. static const U32 bufSize = 256;
  1094. char* ret = Con::getReturnBuffer(bufSize);
  1095. if (object->isServerObject())
  1096. dSprintf(ret, bufSize, "%g %g %g",
  1097. object->mStickyCollisionNormal.x,
  1098. object->mStickyCollisionNormal.y,
  1099. object->mStickyCollisionNormal.z);
  1100. else
  1101. dStrcpy(ret, "0 0 0");
  1102. return ret;
  1103. }
  1104. //----------------------------------------------------------------------------
  1105. bool Item::_setStatic(void *object, const char *index, const char *data)
  1106. {
  1107. Item *i = static_cast<Item*>(object);
  1108. i->mAtRest = dAtob(data);
  1109. i->setMaskBits(InitialUpdateMask | PositionMask);
  1110. return true;
  1111. }
  1112. bool Item::_setRotate(void *object, const char *index, const char *data)
  1113. {
  1114. Item *i = static_cast<Item*>(object);
  1115. i->setMaskBits(InitialUpdateMask | RotationMask);
  1116. return true;
  1117. }
  1118. void Item::initPersistFields()
  1119. {
  1120. addGroup("Misc");
  1121. addProtectedField("static", TypeBool, Offset(mStatic, Item), &_setStatic, &defaultProtectedGetFn, "If true, the object is not moving in the world.\n");
  1122. addProtectedField("rotate", TypeBool, Offset(mRotate, Item), &_setRotate, &defaultProtectedGetFn, "If true, the object will automatically rotate around its Z axis.\n");
  1123. endGroup("Misc");
  1124. Parent::initPersistFields();
  1125. }
  1126. void Item::consoleInit()
  1127. {
  1128. Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks,
  1129. "@brief Fraction of tick at which instant warp occures on the client.\n\n"
  1130. "@ingroup GameObjects");
  1131. Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks,
  1132. "@brief When a warp needs to occur due to the client being too far off from the server, this is the "
  1133. "maximum number of ticks we'll allow the client to warp to catch up.\n\n"
  1134. "@ingroup GameObjects");
  1135. }
  1136. //----------------------------------------------------------------------------
  1137. void Item::prepRenderImage( SceneRenderState* state )
  1138. {
  1139. // Items do NOT render if destroyed
  1140. if (getDamageState() == Destroyed)
  1141. return;
  1142. Parent::prepRenderImage( state );
  1143. }
  1144. void Item::buildConvex(const Box3F& box, Convex* convex)
  1145. {
  1146. if (mShapeInstance == NULL)
  1147. return;
  1148. // These should really come out of a pool
  1149. mConvexList->collectGarbage();
  1150. if (box.isOverlapped(getWorldBox()) == false)
  1151. return;
  1152. // Just return a box convex for the entire shape...
  1153. Convex* cc = 0;
  1154. CollisionWorkingList& wl = convex->getWorkingList();
  1155. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  1156. if (itr->mConvex->getType() == BoxConvexType &&
  1157. itr->mConvex->getObject() == this) {
  1158. cc = itr->mConvex;
  1159. break;
  1160. }
  1161. }
  1162. if (cc)
  1163. return;
  1164. // Create a new convex.
  1165. BoxConvex* cp = new BoxConvex;
  1166. mConvexList->registerObject(cp);
  1167. convex->addToWorkingList(cp);
  1168. cp->init(this);
  1169. mObjBox.getCenter(&cp->mCenter);
  1170. cp->mSize.x = mObjBox.len_x() / 2.0f;
  1171. cp->mSize.y = mObjBox.len_y() / 2.0f;
  1172. cp->mSize.z = mObjBox.len_z() / 2.0f;
  1173. }
  1174. void Item::advanceTime(F32 dt)
  1175. {
  1176. Parent::advanceTime(dt);
  1177. if ( isMounted() )
  1178. return;
  1179. if( mRotate )
  1180. {
  1181. F32 r = (dt / sRotationSpeed) * M_2PI;
  1182. Point3F pos = mRenderObjToWorld.getPosition();
  1183. MatrixF rotMatrix;
  1184. if( mRotate )
  1185. {
  1186. rotMatrix.set( EulerF( 0.0, 0.0, r ) );
  1187. }
  1188. else
  1189. {
  1190. rotMatrix.set( EulerF( r * 0.5, 0.0, r ) );
  1191. }
  1192. MatrixF mat = mRenderObjToWorld;
  1193. mat.setPosition( pos );
  1194. mat.mul( rotMatrix );
  1195. setRenderTransform(mat);
  1196. }
  1197. }