item.cpp 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385
  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. InteriorObjectType | StaticShapeObjectType |
  50. VehicleObjectType | 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, const char* 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. delta.warpTicks = 0;
  273. delta.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,&delta.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. //
  460. if (mCollisionObject && !--mCollisionTimeout)
  461. mCollisionObject = 0;
  462. // Warp to catch up to server
  463. if (delta.warpTicks > 0)
  464. {
  465. delta.warpTicks--;
  466. // Set new pos.
  467. MatrixF mat = mObjToWorld;
  468. mat.getColumn(3,&delta.pos);
  469. delta.pos += delta.warpOffset;
  470. mat.setColumn(3,delta.pos);
  471. Parent::setTransform(mat);
  472. // Backstepping
  473. delta.posVec.x = -delta.warpOffset.x;
  474. delta.posVec.y = -delta.warpOffset.y;
  475. delta.posVec.z = -delta.warpOffset.z;
  476. }
  477. else
  478. {
  479. if (isServerObject() && mAtRest && (mStatic == false && mDataBlock->sticky == false))
  480. {
  481. if (++mAtRestCounter > csmAtRestTimer)
  482. {
  483. mAtRest = false;
  484. mAtRestCounter = 0;
  485. setMaskBits(PositionMask);
  486. }
  487. }
  488. if (!mStatic && !mAtRest && isHidden() == false)
  489. {
  490. updateVelocity(TickSec);
  491. updateWorkingCollisionSet(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
  492. updatePos(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
  493. }
  494. else
  495. {
  496. // Need to clear out last updatePos or warp interpolation
  497. delta.posVec.set(0,0,0);
  498. }
  499. }
  500. }
  501. void Item::interpolateTick(F32 dt)
  502. {
  503. Parent::interpolateTick(dt);
  504. // Client side interpolation
  505. Point3F pos = delta.pos + delta.posVec * dt;
  506. MatrixF mat = mRenderObjToWorld;
  507. mat.setColumn(3,pos);
  508. setRenderTransform(mat);
  509. delta.dt = dt;
  510. }
  511. //----------------------------------------------------------------------------
  512. void Item::setTransform(const MatrixF& mat)
  513. {
  514. Point3F pos;
  515. mat.getColumn(3,&pos);
  516. MatrixF tmat;
  517. if (!mRotate) {
  518. // Forces all rotation to be around the z axis
  519. VectorF vec;
  520. mat.getColumn(1,&vec);
  521. tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y)));
  522. }
  523. else
  524. tmat.identity();
  525. tmat.setColumn(3,pos);
  526. Parent::setTransform(tmat);
  527. if (!mStatic)
  528. {
  529. mAtRest = false;
  530. mAtRestCounter = 0;
  531. }
  532. if ( mPhysicsRep )
  533. mPhysicsRep->setTransform( getTransform() );
  534. setMaskBits(RotationMask | PositionMask | NoWarpMask);
  535. }
  536. //----------------------------------------------------------------------------
  537. void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt)
  538. {
  539. // It is assumed that we will never accelerate more than 10 m/s for gravity...
  540. //
  541. Point3F scaledVelocity = mVelocity * dt;
  542. F32 len = scaledVelocity.len();
  543. F32 newLen = len + (10 * dt);
  544. // Check to see if it is actually necessary to construct the new working list,
  545. // or if we can use the cached version from the last query. We use the x
  546. // component of the min member of the mWorkingQueryBox, which is lame, but
  547. // it works ok.
  548. bool updateSet = false;
  549. Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
  550. F32 l = (newLen * 1.1) + 0.1; // from Convex::updateWorkingList
  551. convexBox.minExtents -= Point3F(l, l, l);
  552. convexBox.maxExtents += Point3F(l, l, l);
  553. // Check containment
  554. {
  555. if (mWorkingQueryBox.minExtents.x != -1e9)
  556. {
  557. if (mWorkingQueryBox.isContained(convexBox) == false)
  558. {
  559. // Needed region is outside the cached region. Update it.
  560. updateSet = true;
  561. }
  562. else
  563. {
  564. // We can leave it alone, we're still inside the cached region
  565. }
  566. }
  567. else
  568. {
  569. // Must update
  570. updateSet = true;
  571. }
  572. }
  573. // Actually perform the query, if necessary
  574. if (updateSet == true)
  575. {
  576. mWorkingQueryBox = convexBox;
  577. mWorkingQueryBox.minExtents -= Point3F(2 * l, 2 * l, 2 * l);
  578. mWorkingQueryBox.maxExtents += Point3F(2 * l, 2 * l, 2 * l);
  579. disableCollision();
  580. if (mCollisionObject)
  581. mCollisionObject->disableCollision();
  582. mConvex.updateWorkingList(mWorkingQueryBox, mask);
  583. if (mCollisionObject)
  584. mCollisionObject->enableCollision();
  585. enableCollision();
  586. }
  587. }
  588. void Item::updateVelocity(const F32 dt)
  589. {
  590. // Acceleration due to gravity
  591. mVelocity.z += (mGravity * mDataBlock->gravityMod) * dt;
  592. F32 len;
  593. if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) {
  594. Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len ));
  595. excess *= 0.1f;
  596. mVelocity -= excess;
  597. }
  598. // Container buoyancy & drag
  599. mVelocity.z -= mBuoyancy * (mGravity * mDataBlock->gravityMod * mGravityMod) * dt;
  600. mVelocity -= mVelocity * mDrag * dt;
  601. }
  602. void Item::updatePos(const U32 /*mask*/, const F32 dt)
  603. {
  604. // Try and move
  605. Point3F pos;
  606. mObjToWorld.getColumn(3,&pos);
  607. delta.posVec = pos;
  608. bool contact = false;
  609. bool nonStatic = false;
  610. bool stickyNotify = false;
  611. CollisionList collisionList;
  612. F32 time = dt;
  613. static Polyhedron sBoxPolyhedron;
  614. static ExtrudedPolyList sExtrudedPolyList;
  615. static EarlyOutPolyList sEarlyOutPolyList;
  616. MatrixF collisionMatrix(true);
  617. Point3F end = pos + mVelocity * time;
  618. U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask;
  619. // Part of our speed problem here is that we don't track contact surfaces, like we do
  620. // with the player. In order to handle the most common and performance impacting
  621. // instance of this problem, we'll use a ray cast to detect any contact surfaces below
  622. // us. This won't be perfect, but it only needs to catch a few of these to make a
  623. // big difference. We'll cast from the top center of the bounding box at the tick's
  624. // beginning to the bottom center of the box at the end.
  625. Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  626. (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
  627. mObjBox.maxExtents.z);
  628. Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
  629. (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
  630. mObjBox.minExtents.z);
  631. collisionMatrix.setColumn(3, pos);
  632. collisionMatrix.mulP(startCast);
  633. collisionMatrix.setColumn(3, end);
  634. collisionMatrix.mulP(endCast);
  635. RayInfo rinfo;
  636. bool doToughCollision = true;
  637. disableCollision();
  638. if (mCollisionObject)
  639. mCollisionObject->disableCollision();
  640. if (getContainer()->castRay(startCast, endCast, mask, &rinfo))
  641. {
  642. F32 bd = -mDot(mVelocity, rinfo.normal);
  643. if (bd >= 0.0)
  644. {
  645. // Contact!
  646. if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
  647. mVelocity.set(0, 0, 0);
  648. mAtRest = true;
  649. mAtRestCounter = 0;
  650. stickyNotify = true;
  651. mStickyCollisionPos = rinfo.point;
  652. mStickyCollisionNormal = rinfo.normal;
  653. doToughCollision = false;;
  654. } else {
  655. // Subtract out velocity into surface and friction
  656. VectorF fv = mVelocity + rinfo.normal * bd;
  657. F32 fvl = fv.len();
  658. if (fvl) {
  659. F32 ff = bd * mDataBlock->friction;
  660. if (ff < fvl) {
  661. fv *= ff / fvl;
  662. fvl = ff;
  663. }
  664. }
  665. bd *= 1 + mDataBlock->elasticity;
  666. VectorF dv = rinfo.normal * (bd + 0.002);
  667. mVelocity += dv;
  668. mVelocity -= fv;
  669. // Keep track of what we hit
  670. contact = true;
  671. U32 typeMask = rinfo.object->getTypeMask();
  672. if (!(typeMask & StaticObjectType))
  673. nonStatic = true;
  674. if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
  675. ShapeBase* col = static_cast<ShapeBase*>(rinfo.object);
  676. queueCollision(col,mVelocity - col->getVelocity());
  677. }
  678. }
  679. }
  680. }
  681. enableCollision();
  682. if (mCollisionObject)
  683. mCollisionObject->enableCollision();
  684. if (doToughCollision)
  685. {
  686. U32 count;
  687. for (count = 0; count < 3; count++)
  688. {
  689. // Build list from convex states here...
  690. end = pos + mVelocity * time;
  691. collisionMatrix.setColumn(3, end);
  692. Box3F wBox = getObjBox();
  693. collisionMatrix.mul(wBox);
  694. Box3F testBox = wBox;
  695. Point3F oldMin = testBox.minExtents;
  696. Point3F oldMax = testBox.maxExtents;
  697. testBox.minExtents.setMin(oldMin + (mVelocity * time));
  698. testBox.maxExtents.setMin(oldMax + (mVelocity * time));
  699. sEarlyOutPolyList.clear();
  700. sEarlyOutPolyList.mNormal.set(0,0,0);
  701. sEarlyOutPolyList.mPlaneList.setSize(6);
  702. sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0));
  703. sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0));
  704. sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0));
  705. sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0));
  706. sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1));
  707. sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1));
  708. CollisionWorkingList& eorList = mConvex.getWorkingList();
  709. CollisionWorkingList* eopList = eorList.wLink.mNext;
  710. while (eopList != &eorList) {
  711. if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0)
  712. {
  713. Box3F convexBox = eopList->mConvex->getBoundingBox();
  714. if (testBox.isOverlapped(convexBox))
  715. {
  716. eopList->mConvex->getPolyList(&sEarlyOutPolyList);
  717. if (sEarlyOutPolyList.isEmpty() == false)
  718. break;
  719. }
  720. }
  721. eopList = eopList->wLink.mNext;
  722. }
  723. if (sEarlyOutPolyList.isEmpty())
  724. {
  725. pos = end;
  726. break;
  727. }
  728. collisionMatrix.setColumn(3, pos);
  729. sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true);
  730. // Build extruded polyList...
  731. VectorF vector = end - pos;
  732. sExtrudedPolyList.extrude(sBoxPolyhedron, vector);
  733. sExtrudedPolyList.setVelocity(mVelocity);
  734. sExtrudedPolyList.setCollisionList(&collisionList);
  735. CollisionWorkingList& rList = mConvex.getWorkingList();
  736. CollisionWorkingList* pList = rList.wLink.mNext;
  737. while (pList != &rList) {
  738. if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0)
  739. {
  740. Box3F convexBox = pList->mConvex->getBoundingBox();
  741. if (testBox.isOverlapped(convexBox))
  742. {
  743. pList->mConvex->getPolyList(&sExtrudedPolyList);
  744. }
  745. }
  746. pList = pList->wLink.mNext;
  747. }
  748. if (collisionList.getTime() < 1.0)
  749. {
  750. // Set to collision point
  751. F32 dt = time * collisionList.getTime();
  752. pos += mVelocity * dt;
  753. time -= dt;
  754. // Pick the most resistant surface
  755. F32 bd = 0;
  756. const Collision* collision = 0;
  757. for (int c = 0; c < collisionList.getCount(); c++) {
  758. const Collision &cp = collisionList[c];
  759. F32 dot = -mDot(mVelocity,cp.normal);
  760. if (dot > bd) {
  761. bd = dot;
  762. collision = &cp;
  763. }
  764. }
  765. if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
  766. mVelocity.set(0, 0, 0);
  767. mAtRest = true;
  768. mAtRestCounter = 0;
  769. stickyNotify = true;
  770. mStickyCollisionPos = collision->point;
  771. mStickyCollisionNormal = collision->normal;
  772. break;
  773. } else {
  774. // Subtract out velocity into surface and friction
  775. if (collision) {
  776. VectorF fv = mVelocity + collision->normal * bd;
  777. F32 fvl = fv.len();
  778. if (fvl) {
  779. F32 ff = bd * mDataBlock->friction;
  780. if (ff < fvl) {
  781. fv *= ff / fvl;
  782. fvl = ff;
  783. }
  784. }
  785. bd *= 1 + mDataBlock->elasticity;
  786. VectorF dv = collision->normal * (bd + 0.002);
  787. mVelocity += dv;
  788. mVelocity -= fv;
  789. // Keep track of what we hit
  790. contact = true;
  791. U32 typeMask = collision->object->getTypeMask();
  792. if (!(typeMask & StaticObjectType))
  793. nonStatic = true;
  794. if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
  795. ShapeBase* col = static_cast<ShapeBase*>(collision->object);
  796. queueCollision(col,mVelocity - col->getVelocity());
  797. }
  798. }
  799. }
  800. }
  801. else
  802. {
  803. pos = end;
  804. break;
  805. }
  806. }
  807. if (count == 3)
  808. {
  809. // Couldn't move...
  810. mVelocity.set(0, 0, 0);
  811. }
  812. }
  813. // If on the client, calculate delta for backstepping
  814. if (isGhost()) {
  815. delta.pos = pos;
  816. delta.posVec -= pos;
  817. delta.dt = 1;
  818. }
  819. // Update transform
  820. MatrixF mat = mObjToWorld;
  821. mat.setColumn(3,pos);
  822. Parent::setTransform(mat);
  823. enableCollision();
  824. if (mCollisionObject)
  825. mCollisionObject->enableCollision();
  826. updateContainer();
  827. if ( mPhysicsRep )
  828. mPhysicsRep->setTransform( mat );
  829. //
  830. if (contact) {
  831. // Check for rest condition
  832. if (!nonStatic && mVelocity.len() < sAtRestVelocity) {
  833. mVelocity.x = mVelocity.y = mVelocity.z = 0;
  834. mAtRest = true;
  835. mAtRestCounter = 0;
  836. }
  837. // Only update the client if we hit a non-static shape or
  838. // if this is our final rest pos.
  839. if (nonStatic || mAtRest)
  840. setMaskBits(PositionMask);
  841. }
  842. // Collision callbacks. These need to be processed whether we hit
  843. // anything or not.
  844. if (!isGhost())
  845. {
  846. SimObjectPtr<Item> safePtr(this);
  847. if (stickyNotify)
  848. {
  849. notifyCollision();
  850. if(bool(safePtr))
  851. onStickyCollision_callback( getIdString() );
  852. }
  853. else
  854. notifyCollision();
  855. // water
  856. if(bool(safePtr))
  857. {
  858. if(!mInLiquid && mWaterCoverage != 0.0f)
  859. {
  860. onEnterLiquid_callback( getIdString(), Con::getFloatArg(mWaterCoverage), mLiquidType.c_str() );
  861. mInLiquid = true;
  862. }
  863. else if(mInLiquid && mWaterCoverage == 0.0f)
  864. {
  865. onLeaveLiquid_callback(getIdString(), mLiquidType.c_str());
  866. mInLiquid = false;
  867. }
  868. }
  869. }
  870. }
  871. //----------------------------------------------------------------------------
  872. static MatrixF IMat(1);
  873. bool Item::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF&)
  874. {
  875. if ( context == PLC_Decal )
  876. return false;
  877. // Collision with the item is always against the item's object
  878. // space bounding box axis aligned in world space.
  879. Point3F pos;
  880. mObjToWorld.getColumn(3,&pos);
  881. IMat.setColumn(3,pos);
  882. polyList->setTransform(&IMat, mObjScale);
  883. polyList->setObject(this);
  884. polyList->addBox(mObjBox);
  885. return true;
  886. }
  887. //----------------------------------------------------------------------------
  888. U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
  889. {
  890. U32 retMask = Parent::packUpdate(connection,mask,stream);
  891. if (stream->writeFlag(mask & InitialUpdateMask)) {
  892. stream->writeFlag(mRotate);
  893. stream->writeFlag(mStatic);
  894. if (stream->writeFlag(getScale() != Point3F(1, 1, 1)))
  895. mathWrite(*stream, getScale());
  896. }
  897. if (mask & ThrowSrcMask && mCollisionObject) {
  898. S32 gIndex = connection->getGhostIndex(mCollisionObject);
  899. if (stream->writeFlag(gIndex != -1))
  900. stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
  901. }
  902. else
  903. stream->writeFlag(false);
  904. if (stream->writeFlag(mask & RotationMask && !mRotate)) {
  905. // Assumes rotation is about the Z axis
  906. AngAxisF aa(mObjToWorld);
  907. stream->writeFlag(aa.axis.z < 0);
  908. stream->write(aa.angle);
  909. }
  910. if (stream->writeFlag(mask & PositionMask)) {
  911. Point3F pos;
  912. mObjToWorld.getColumn(3,&pos);
  913. mathWrite(*stream, pos);
  914. if (!stream->writeFlag(mAtRest)) {
  915. mathWrite(*stream, mVelocity);
  916. }
  917. stream->writeFlag(!(mask & NoWarpMask));
  918. }
  919. return retMask;
  920. }
  921. void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
  922. {
  923. Parent::unpackUpdate(connection,stream);
  924. // InitialUpdateMask
  925. if (stream->readFlag()) {
  926. mRotate = stream->readFlag();
  927. mStatic = stream->readFlag();
  928. if (stream->readFlag())
  929. mathRead(*stream, &mObjScale);
  930. else
  931. mObjScale.set(1, 1, 1);
  932. }
  933. // ThrowSrcMask && mCollisionObject
  934. if (stream->readFlag()) {
  935. S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
  936. setCollisionTimeout(static_cast<ShapeBase*>(connection->resolveGhost(gIndex)));
  937. }
  938. MatrixF mat = mObjToWorld;
  939. // RotationMask && !mRotate
  940. if (stream->readFlag()) {
  941. // Assumes rotation is about the Z axis
  942. AngAxisF aa;
  943. aa.axis.set(0.0f, 0.0f, stream->readFlag() ? -1.0f : 1.0f);
  944. stream->read(&aa.angle);
  945. aa.setMatrix(&mat);
  946. Point3F pos;
  947. mObjToWorld.getColumn(3,&pos);
  948. mat.setColumn(3,pos);
  949. }
  950. // PositionMask
  951. if (stream->readFlag()) {
  952. Point3F pos;
  953. mathRead(*stream, &pos);
  954. F32 speed = mVelocity.len();
  955. if ((mAtRest = stream->readFlag()) == true)
  956. mVelocity.set(0.0f, 0.0f, 0.0f);
  957. else
  958. mathRead(*stream, &mVelocity);
  959. if (stream->readFlag() && isProperlyAdded()) {
  960. // Determin number of ticks to warp based on the average
  961. // of the client and server velocities.
  962. delta.warpOffset = pos - delta.pos;
  963. F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
  964. F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
  965. delta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5f), 1.0f): 0.0f);
  966. if (delta.warpTicks)
  967. {
  968. // Setup the warp to start on the next tick, only the
  969. // object's position is warped.
  970. if (delta.warpTicks > sMaxWarpTicks)
  971. delta.warpTicks = sMaxWarpTicks;
  972. delta.warpOffset /= (F32)delta.warpTicks;
  973. }
  974. else {
  975. // Going to skip the warp, server and client are real close.
  976. // Adjust the frame interpolation to move smoothly to the
  977. // new position within the current tick.
  978. Point3F cp = delta.pos + delta.posVec * delta.dt;
  979. VectorF vec = delta.pos - cp;
  980. F32 vl = vec.len();
  981. if (vl) {
  982. F32 s = delta.posVec.len() / vl;
  983. delta.posVec = (cp - pos) * s;
  984. }
  985. delta.pos = pos;
  986. mat.setColumn(3,pos);
  987. }
  988. }
  989. else {
  990. // Set the item to the server position
  991. delta.warpTicks = 0;
  992. delta.posVec.set(0,0,0);
  993. delta.pos = pos;
  994. delta.dt = 0;
  995. mat.setColumn(3,pos);
  996. }
  997. }
  998. Parent::setTransform(mat);
  999. }
  1000. DefineEngineMethod( Item, isStatic, bool, (),,
  1001. "@brief Is the object static (ie, non-movable)?\n\n"
  1002. "@return True if the object is static, false if it is not.\n"
  1003. "@tsexample\n"
  1004. "// Query the item on if it is or is not static.\n"
  1005. "%isStatic = %itemData.isStatic();\n\n"
  1006. "@endtsexample\n\n"
  1007. "@see static\n"
  1008. )
  1009. {
  1010. return object->isStatic();
  1011. }
  1012. DefineEngineMethod( Item, isAtRest, bool, (),,
  1013. "@brief Is the object at rest (ie, no longer moving)?\n\n"
  1014. "@return True if the object is at rest, false if it is not.\n"
  1015. "@tsexample\n"
  1016. "// Query the item on if it is or is not at rest.\n"
  1017. "%isAtRest = %item.isAtRest();\n\n"
  1018. "@endtsexample\n\n"
  1019. )
  1020. {
  1021. return object->isAtRest();
  1022. }
  1023. DefineEngineMethod( Item, isRotating, bool, (),,
  1024. "@brief Is the object still rotating?\n\n"
  1025. "@return True if the object is still rotating, false if it is not.\n"
  1026. "@tsexample\n"
  1027. "// Query the item on if it is or is not rotating.\n"
  1028. "%isRotating = %itemData.isRotating();\n\n"
  1029. "@endtsexample\n\n"
  1030. "@see rotate\n"
  1031. )
  1032. {
  1033. return object->isRotating();
  1034. }
  1035. DefineEngineMethod( Item, setCollisionTimeout, bool, (int ignoreColObj),(NULL),
  1036. "@brief Temporarily disable collisions against a specific ShapeBase object.\n\n"
  1037. "This is useful to prevent a player from immediately picking up an Item they have "
  1038. "just thrown. Only one object may be on the timeout list at a time. The timeout is "
  1039. "defined as 15 ticks.\n\n"
  1040. "@param objectID ShapeBase object ID to disable collisions against.\n"
  1041. "@return Returns true if the ShapeBase object requested could be found, false if it could not.\n"
  1042. "@tsexample\n"
  1043. "// Set the ShapeBase Object ID to disable collisions against\n"
  1044. "%ignoreColObj = %player.getID();\n\n"
  1045. "// Inform this Item object to ignore collisions temproarily against the %ignoreColObj.\n"
  1046. "%item.setCollisionTimeout(%ignoreColObj);\n\n"
  1047. "@endtsexample\n\n"
  1048. )
  1049. {
  1050. ShapeBase* source = NULL;
  1051. if (Sim::findObject(ignoreColObj,source)) {
  1052. object->setCollisionTimeout(source);
  1053. return true;
  1054. }
  1055. return false;
  1056. }
  1057. DefineEngineMethod( Item, getLastStickyPos, const char*, (),,
  1058. "@brief Get the position on the surface on which this Item is stuck.\n\n"
  1059. "@return Returns The XYZ position of where this Item is stuck.\n"
  1060. "@tsexample\n"
  1061. "// Acquire the position where this Item is currently stuck\n"
  1062. "%stuckPosition = %item.getLastStickPos();\n\n"
  1063. "@endtsexample\n\n"
  1064. "@note Server side only.\n"
  1065. )
  1066. {
  1067. char* ret = Con::getReturnBuffer(256);
  1068. if (object->isServerObject())
  1069. dSprintf(ret, 255, "%g %g %g",
  1070. object->mStickyCollisionPos.x,
  1071. object->mStickyCollisionPos.y,
  1072. object->mStickyCollisionPos.z);
  1073. else
  1074. dStrcpy(ret, "0 0 0");
  1075. return ret;
  1076. }
  1077. DefineEngineMethod( Item, getLastStickyNormal, const char *, (),,
  1078. "@brief Get the normal of the surface on which the object is stuck.\n\n"
  1079. "@return Returns The XYZ normal from where this Item is stuck.\n"
  1080. "@tsexample\n"
  1081. "// Acquire the position where this Item is currently stuck\n"
  1082. "%stuckPosition = %item.getLastStickPos();\n\n"
  1083. "@endtsexample\n\n"
  1084. "@note Server side only.\n"
  1085. )
  1086. {
  1087. char* ret = Con::getReturnBuffer(256);
  1088. if (object->isServerObject())
  1089. dSprintf(ret, 255, "%g %g %g",
  1090. object->mStickyCollisionNormal.x,
  1091. object->mStickyCollisionNormal.y,
  1092. object->mStickyCollisionNormal.z);
  1093. else
  1094. dStrcpy(ret, "0 0 0");
  1095. return ret;
  1096. }
  1097. //----------------------------------------------------------------------------
  1098. bool Item::_setStatic(void *object, const char *index, const char *data)
  1099. {
  1100. Item *i = static_cast<Item*>(object);
  1101. i->mAtRest = dAtob(data);
  1102. i->setMaskBits(InitialUpdateMask | PositionMask);
  1103. return true;
  1104. }
  1105. bool Item::_setRotate(void *object, const char *index, const char *data)
  1106. {
  1107. Item *i = static_cast<Item*>(object);
  1108. i->setMaskBits(InitialUpdateMask | RotationMask);
  1109. return true;
  1110. }
  1111. void Item::initPersistFields()
  1112. {
  1113. addGroup("Misc");
  1114. addProtectedField("static", TypeBool, Offset(mStatic, Item), &_setStatic, &defaultProtectedGetFn, "If true, the object is not moving in the world.\n");
  1115. addProtectedField("rotate", TypeBool, Offset(mRotate, Item), &_setRotate, &defaultProtectedGetFn, "If true, the object will automatically rotate around its Z axis.\n");
  1116. endGroup("Misc");
  1117. Parent::initPersistFields();
  1118. }
  1119. void Item::consoleInit()
  1120. {
  1121. Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks,
  1122. "@brief Fraction of tick at which instant warp occures on the client.\n\n"
  1123. "@ingroup GameObjects");
  1124. Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks,
  1125. "@brief When a warp needs to occur due to the client being too far off from the server, this is the "
  1126. "maximum number of ticks we'll allow the client to warp to catch up.\n\n"
  1127. "@ingroup GameObjects");
  1128. }
  1129. //----------------------------------------------------------------------------
  1130. void Item::prepRenderImage( SceneRenderState* state )
  1131. {
  1132. // Items do NOT render if destroyed
  1133. if (getDamageState() == Destroyed)
  1134. return;
  1135. Parent::prepRenderImage( state );
  1136. }
  1137. void Item::buildConvex(const Box3F& box, Convex* convex)
  1138. {
  1139. if (mShapeInstance == NULL)
  1140. return;
  1141. // These should really come out of a pool
  1142. mConvexList->collectGarbage();
  1143. if (box.isOverlapped(getWorldBox()) == false)
  1144. return;
  1145. // Just return a box convex for the entire shape...
  1146. Convex* cc = 0;
  1147. CollisionWorkingList& wl = convex->getWorkingList();
  1148. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  1149. if (itr->mConvex->getType() == BoxConvexType &&
  1150. itr->mConvex->getObject() == this) {
  1151. cc = itr->mConvex;
  1152. break;
  1153. }
  1154. }
  1155. if (cc)
  1156. return;
  1157. // Create a new convex.
  1158. BoxConvex* cp = new BoxConvex;
  1159. mConvexList->registerObject(cp);
  1160. convex->addToWorkingList(cp);
  1161. cp->init(this);
  1162. mObjBox.getCenter(&cp->mCenter);
  1163. cp->mSize.x = mObjBox.len_x() / 2.0f;
  1164. cp->mSize.y = mObjBox.len_y() / 2.0f;
  1165. cp->mSize.z = mObjBox.len_z() / 2.0f;
  1166. }
  1167. void Item::advanceTime(F32 dt)
  1168. {
  1169. Parent::advanceTime(dt);
  1170. if( mRotate )
  1171. {
  1172. F32 r = (dt / sRotationSpeed) * M_2PI;
  1173. Point3F pos = mRenderObjToWorld.getPosition();
  1174. MatrixF rotMatrix;
  1175. if( mRotate )
  1176. {
  1177. rotMatrix.set( EulerF( 0.0, 0.0, r ) );
  1178. }
  1179. else
  1180. {
  1181. rotMatrix.set( EulerF( r * 0.5, 0.0, r ) );
  1182. }
  1183. MatrixF mat = mRenderObjToWorld;
  1184. mat.setPosition( pos );
  1185. mat.mul( rotMatrix );
  1186. setRenderTransform(mat);
  1187. }
  1188. }