item.cpp 43 KB

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