item.cpp 44 KB

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