item.cpp 44 KB

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