item.cpp 43 KB

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