rigidShape.cpp 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739
  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/rigidShape.h"
  24. #include "app/game.h"
  25. #include "math/mMath.h"
  26. #include "console/simBase.h"
  27. #include "console/console.h"
  28. #include "console/consoleTypes.h"
  29. #include "collision/clippedPolyList.h"
  30. #include "collision/planeExtractor.h"
  31. #include "T3D/gameBase/moveManager.h"
  32. #include "core/stream/bitStream.h"
  33. #include "core/dnet.h"
  34. #include "T3D/gameBase/gameConnection.h"
  35. #include "ts/tsShapeInstance.h"
  36. #include "math/mathIO.h"
  37. #include "scene/sceneRenderState.h"
  38. #include "scene/sceneManager.h"
  39. #include "T3D/fx/cameraFXMgr.h"
  40. #include "T3D/trigger.h"
  41. #include "T3D/item.h"
  42. #include "gfx/primBuilder.h"
  43. #include "gfx/gfxDrawUtil.h"
  44. #include "sfx/sfxTypes.h"
  45. #include "sfx/sfxSystem.h"
  46. #include "T3D/fx/particleEmitter.h"
  47. #include "console/engineAPI.h"
  48. IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData);
  49. ConsoleDocClass( RigidShapeData,
  50. "@brief Defines the physics properties for an individual RigidShapeData physics object.\n\n"
  51. "@tsexample\n"
  52. " datablock RigidShapeData( BouncingBoulder )\n"
  53. " {\n"
  54. " category = \"RigidShape\";\n"
  55. "\n"
  56. " shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n"
  57. " emap = true;\n"
  58. "\n"
  59. " // Rigid Body\n"
  60. " mass = 500;\n"
  61. " massCenter = \"0 0 0\"; // Center of mass for rigid body\n"
  62. " massBox = \"0 0 0\"; // Size of box used for moment of inertia,\n"
  63. " // if zero it defaults to object bounding box\n"
  64. " drag = 0.2; // Drag coefficient\n"
  65. " bodyFriction = 0.2;\n"
  66. " bodyRestitution = 0.1;\n"
  67. " minImpactSpeed = 5; // Impacts over this invoke the script callback\n"
  68. " softImpactSpeed = 5; // Play SoftImpact Sound\n"
  69. " hardImpactSpeed = 15; // Play HardImpact Sound\n"
  70. " integration = 4; // Physics integration: TickSec/Rate\n"
  71. " collisionTol = 0.1; // Collision distance tolerance\n"
  72. " contactTol = 0.1; // Contact velocity tolerance\n"
  73. "\n"
  74. " minRollSpeed = 10;\n"
  75. "\n"
  76. " maxDrag = 0.5;\n"
  77. " minDrag = 0.01;\n"
  78. "\n"
  79. " dustHeight = 10;\n"
  80. "\n"
  81. " dragForce = 0.05;\n"
  82. " vertFactor = 0.05;\n"
  83. " };\n"
  84. "@endtsexample\n\n"
  85. "@see RigidShape\n"
  86. "@see ShapeBase\n\n"
  87. "@ingroup Physics\n"
  88. );
  89. IMPLEMENT_CO_NETOBJECT_V1(RigidShape);
  90. ConsoleDocClass( RigidShape,
  91. "@brief The RigidShape class implements rigid-body physics for DTS objects in the world.\n\n"
  92. "\"Rigid body physics\" refers to a system whereby objects are assumed to have a finite size,\n"
  93. "equally distributed masses, and where deformations of the objects themselves are not accounted for.\n"
  94. "Uses the RigidShape class to control its physics.\n\n"
  95. "@tsexample\n"
  96. " datablock RigidShapeData( BouncingBoulder )\n"
  97. " {\n"
  98. " category = \"RigidShape\";\n"
  99. "\n"
  100. " shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n"
  101. " emap = true;\n"
  102. "\n"
  103. " // Rigid Body\n"
  104. " mass = 500;\n"
  105. " massCenter = \"0 0 0\"; // Center of mass for rigid body\n"
  106. " massBox = \"0 0 0\"; // Size of box used for moment of inertia,\n"
  107. " // if zero it defaults to object bounding box\n"
  108. " drag = 0.2; // Drag coefficient\n"
  109. " bodyFriction = 0.2;\n"
  110. " bodyRestitution = 0.1;\n"
  111. " minImpactSpeed = 5; // Impacts over this invoke the script callback\n"
  112. " softImpactSpeed = 5; // Play SoftImpact Sound\n"
  113. " hardImpactSpeed = 15; // Play HardImpact Sound\n"
  114. " integration = 4; // Physics integration: TickSec/Rate\n"
  115. " collisionTol = 0.1; // Collision distance tolerance\n"
  116. " contactTol = 0.1; // Contact velocity tolerance\n"
  117. "\n"
  118. " minRollSpeed = 10;\n"
  119. "\n"
  120. " maxDrag = 0.5;\n"
  121. " minDrag = 0.01;\n"
  122. "\n"
  123. " dustHeight = 10;\n"
  124. "\n"
  125. " dragForce = 0.05;\n"
  126. " vertFactor = 0.05;\n"
  127. " };\n"
  128. "\n"
  129. " new RigidShape()\n"
  130. " {\n"
  131. " dataBlock = \"BouncingBoulder\";\n"
  132. " parentGroup = EWCreatorWindow.objectGroup;\n"
  133. " };\n"
  134. "@endtsexample\n\n"
  135. "@see RigidShapeData\n"
  136. "@see ShapeBase\n\n"
  137. "@ingroup Physics\n"
  138. );
  139. IMPLEMENT_CALLBACK( RigidShape, onEnterLiquid, void, ( const char* objId, F32 waterCoverage, const char* liquidType ),
  140. ( objId, waterCoverage, liquidType ),
  141. "@brief Called whenever this RigidShape object enters liquid.\n\n"
  142. "@param objId The ID of the rigidShape object.\n"
  143. "@param waterCoverage Amount of water coverage the RigidShape has.\n"
  144. "@param liquidType Type of liquid that was entered.\n\n"
  145. "@tsexample\n"
  146. "// The RigidShape object falls in a body of liquid, causing the callback to occur.\n"
  147. "RigidShape::onEnterLiquid(%this,%objId,%waterCoverage,%liquidType)\n"
  148. " {\n"
  149. " // Code to run whenever this callback occurs.\n"
  150. " }\n"
  151. "@endtsexample\n\n"
  152. "@see ShapeBase\n\n"
  153. );
  154. IMPLEMENT_CALLBACK( RigidShape, onLeaveLiquid, void, ( const char* objId, const char* liquidType ),( objId, liquidType ),
  155. "@brief Called whenever the RigidShape object exits liquid.\n\n"
  156. "@param objId The ID of the RigidShape object.\n"
  157. "@param liquidType Type if liquid that was exited.\n\n"
  158. "@tsexample\n"
  159. "// The RigidShape object exits in a body of liquid, causing the callback to occur.\n"
  160. "RigidShape::onLeaveLiquid(%this,%objId,%liquidType)\n"
  161. " {\n"
  162. " // Code to run whenever this callback occurs.\n"
  163. " }\n"
  164. "@endtsexample\n\n"
  165. "@see ShapeBase\n\n"
  166. );
  167. //----------------------------------------------------------------------------
  168. namespace {
  169. // Client prediction
  170. const S32 sMaxWarpTicks = 3; // Max warp duration in ticks
  171. const S32 sMaxPredictionTicks = 30; // Number of ticks to predict
  172. const F32 sRigidShapeGravity = -20;
  173. // Physics and collision constants
  174. static F32 sRestTol = 0.5; // % of gravity energy to be at rest
  175. static S32 sRestCount = 10; // Consecutive ticks before comming to rest
  176. const U32 sCollisionMoveMask = ( TerrainObjectType | PlayerObjectType |
  177. StaticShapeObjectType | VehicleObjectType |
  178. VehicleBlockerObjectType );
  179. const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
  180. const U32 sClientCollisionMask = sCollisionMoveMask;
  181. void nonFilter(SceneObject* object,void *key)
  182. {
  183. SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
  184. object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
  185. }
  186. } // namespace {}
  187. // Trigger objects that are not normally collided with.
  188. static U32 sTriggerMask = ItemObjectType |
  189. TriggerObjectType |
  190. CorpseObjectType;
  191. //----------------------------------------------------------------------------
  192. RigidShapeData::RigidShapeData()
  193. {
  194. shadowEnable = true;
  195. body.friction = 0;
  196. body.restitution = 1;
  197. minImpactSpeed = 25;
  198. softImpactSpeed = 25;
  199. hardImpactSpeed = 50;
  200. minRollSpeed = 0;
  201. cameraRoll = true;
  202. cameraLag = 0;
  203. cameraDecay = 0;
  204. cameraOffset = 0;
  205. minDrag = 0;
  206. maxDrag = 0;
  207. integration = 1;
  208. collisionTol = 0.1f;
  209. contactTol = 0.1f;
  210. massCenter.set(0,0,0);
  211. massBox.set(0,0,0);
  212. drag = 0.7f;
  213. density = 4;
  214. for (S32 i = 0; i < Body::MaxSounds; i++)
  215. body.sound[i] = 0;
  216. dustEmitter = NULL;
  217. dustID = 0;
  218. dustHeight = 1.0;
  219. dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) );
  220. dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) );
  221. splashFreqMod = 300.0;
  222. splashVelEpsilon = 0.50;
  223. exitSplashSoundVel = 2.0;
  224. softSplashSoundVel = 1.0;
  225. medSplashSoundVel = 2.0;
  226. hardSplashSoundVel = 3.0;
  227. dMemset(waterSound, 0, sizeof(waterSound));
  228. dragForce = 0;
  229. vertFactor = 0.25;
  230. dustTrailEmitter = NULL;
  231. dustTrailID = 0;
  232. }
  233. RigidShapeData::~RigidShapeData()
  234. {
  235. }
  236. //----------------------------------------------------------------------------
  237. bool RigidShapeData::onAdd()
  238. {
  239. if(!Parent::onAdd())
  240. return false;
  241. return true;
  242. }
  243. bool RigidShapeData::preload(bool server, String &errorStr)
  244. {
  245. if (!Parent::preload(server, errorStr))
  246. return false;
  247. // RigidShape objects must define a collision detail
  248. if (!collisionDetails.size() || collisionDetails[0] == -1)
  249. {
  250. Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail");
  251. errorStr = String::ToString("RigidShapeData: Couldn't load shape \"%s\"",shapeName);
  252. return false;
  253. }
  254. // Resolve objects transmitted from server
  255. if (!server) {
  256. for (S32 i = 0; i < Body::MaxSounds; i++)
  257. sfxResolve( &body.sound[ i ], errorStr );
  258. }
  259. if( !dustEmitter && dustID != 0 )
  260. {
  261. if( !Sim::findObject( dustID, dustEmitter ) )
  262. {
  263. Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID );
  264. }
  265. }
  266. U32 i;
  267. for( i=0; i<VC_NUM_SPLASH_EMITTERS; i++ )
  268. {
  269. if( !splashEmitterList[i] && splashEmitterIDList[i] != 0 )
  270. {
  271. if( !Sim::findObject( splashEmitterIDList[i], splashEmitterList[i] ) )
  272. {
  273. Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(splashEmitter): 0x%x", splashEmitterIDList[i] );
  274. }
  275. }
  276. }
  277. if (dragForce <= 0.01f)
  278. {
  279. Con::warnf("RigidShapeData::preload: dragForce must be at least 0.01");
  280. dragForce = 0.01f;
  281. }
  282. if (vertFactor < 0.0f || vertFactor > 1.0f)
  283. {
  284. Con::warnf("RigidShapeData::preload: vert factor must be [0, 1]");
  285. vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f;
  286. }
  287. if( !dustTrailEmitter && dustTrailID != 0 )
  288. {
  289. if( !Sim::findObject( dustTrailID, dustTrailEmitter ) )
  290. {
  291. Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID );
  292. }
  293. }
  294. return true;
  295. }
  296. //----------------------------------------------------------------------------
  297. void RigidShapeData::packData(BitStream* stream)
  298. {
  299. Parent::packData(stream);
  300. stream->write(body.restitution);
  301. stream->write(body.friction);
  302. for( U32 i = 0; i < Body::MaxSounds; ++ i )
  303. sfxWrite( stream, body.sound[ i ] );
  304. stream->write(minImpactSpeed);
  305. stream->write(softImpactSpeed);
  306. stream->write(hardImpactSpeed);
  307. stream->write(minRollSpeed);
  308. stream->write(maxDrag);
  309. stream->write(minDrag);
  310. stream->write(integration);
  311. stream->write(collisionTol);
  312. stream->write(contactTol);
  313. mathWrite(*stream,massCenter);
  314. mathWrite(*stream,massBox);
  315. stream->writeFlag(cameraRoll);
  316. stream->write(cameraLag);
  317. stream->write(cameraDecay);
  318. stream->write(cameraOffset);
  319. stream->write( dustHeight );
  320. stream->write(exitSplashSoundVel);
  321. stream->write(softSplashSoundVel);
  322. stream->write(medSplashSoundVel);
  323. stream->write(hardSplashSoundVel);
  324. // write the water sound profiles
  325. for( U32 i = 0; i < MaxSounds; ++ i )
  326. sfxWrite( stream, waterSound[ i ] );
  327. if (stream->writeFlag( dustEmitter ))
  328. stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  329. for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i )
  330. {
  331. if( stream->writeFlag( splashEmitterList[i] != NULL ) )
  332. stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  333. }
  334. stream->write(splashFreqMod);
  335. stream->write(splashVelEpsilon);
  336. stream->write(dragForce);
  337. stream->write(vertFactor);
  338. if (stream->writeFlag( dustTrailEmitter ))
  339. stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  340. }
  341. void RigidShapeData::unpackData(BitStream* stream)
  342. {
  343. Parent::unpackData(stream);
  344. stream->read(&body.restitution);
  345. stream->read(&body.friction);
  346. for( U32 i = 0; i < Body::MaxSounds; i++)
  347. sfxRead( stream, &body.sound[ i ] );
  348. stream->read(&minImpactSpeed);
  349. stream->read(&softImpactSpeed);
  350. stream->read(&hardImpactSpeed);
  351. stream->read(&minRollSpeed);
  352. stream->read(&maxDrag);
  353. stream->read(&minDrag);
  354. stream->read(&integration);
  355. stream->read(&collisionTol);
  356. stream->read(&contactTol);
  357. mathRead(*stream,&massCenter);
  358. mathRead(*stream,&massBox);
  359. cameraRoll = stream->readFlag();
  360. stream->read(&cameraLag);
  361. stream->read(&cameraDecay);
  362. stream->read(&cameraOffset);
  363. stream->read( &dustHeight );
  364. stream->read(&exitSplashSoundVel);
  365. stream->read(&softSplashSoundVel);
  366. stream->read(&medSplashSoundVel);
  367. stream->read(&hardSplashSoundVel);
  368. // write the water sound profiles
  369. for( U32 i = 0; i < MaxSounds; ++ i )
  370. sfxRead( stream, &waterSound[ i ] );
  371. if( stream->readFlag() )
  372. dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
  373. for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i )
  374. {
  375. if( stream->readFlag() )
  376. splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  377. }
  378. stream->read(&splashFreqMod);
  379. stream->read(&splashVelEpsilon);
  380. stream->read(&dragForce);
  381. stream->read(&vertFactor);
  382. if( stream->readFlag() )
  383. dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
  384. }
  385. //----------------------------------------------------------------------------
  386. void RigidShapeData::initPersistFields()
  387. {
  388. addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body.");
  389. addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box.");
  390. addField("bodyRestitution", TypeF32, Offset(body.restitution, RigidShapeData), "The percentage of kinetic energy kept by this object in a collision.");
  391. addField("bodyFriction", TypeF32, Offset(body.friction, RigidShapeData), "How much friction this object has. Lower values will cause the object to appear to be more slippery.");
  392. addField("minImpactSpeed", TypeF32, Offset(minImpactSpeed, RigidShapeData),
  393. "Minimum collision speed to classify collision as impact (triggers onImpact on server object)." );
  394. addField("softImpactSpeed", TypeF32, Offset(softImpactSpeed, RigidShapeData), "Minimum speed at which this object must be travelling for the soft impact sound to be played.");
  395. addField("hardImpactSpeed", TypeF32, Offset(hardImpactSpeed, RigidShapeData), "Minimum speed at which the object must be travelling for the hard impact sound to be played.");
  396. addField("minRollSpeed", TypeF32, Offset(minRollSpeed, RigidShapeData));
  397. addField("maxDrag", TypeF32, Offset(maxDrag, RigidShapeData), "Maximum drag available to this object.");
  398. addField("minDrag", TypeF32, Offset(minDrag, RigidShapeData), "Minimum drag available to this object.");
  399. addField("integration", TypeS32, Offset(integration, RigidShapeData), "Number of physics steps to process per tick.");
  400. addField("collisionTol", TypeF32, Offset(collisionTol, RigidShapeData), "Collision distance tolerance.");
  401. addField("contactTol", TypeF32, Offset(contactTol, RigidShapeData), "Contact velocity tolerance.");
  402. addGroup( "Forces" );
  403. addField("dragForce", TypeF32, Offset(dragForce, RigidShapeData), "Used to simulate the constant drag acting on the object");
  404. addField("vertFactor", TypeF32, Offset(vertFactor, RigidShapeData), "The scalar applied to the vertical portion of the velocity drag acting on a object.");
  405. endGroup( "Forces" );
  406. addGroup( "Particle Effects" );
  407. addField("dustEmitter", TYPEID< ParticleEmitterData >(), Offset(dustEmitter, RigidShapeData), "Array of pointers to ParticleEmitterData datablocks which will be used to emit particles at object/terrain contact point.\n");
  408. addField("triggerDustHeight", TypeF32, Offset(triggerDustHeight, RigidShapeData), "Maximum height from the ground at which the object will generate dust.\n");
  409. addField("dustHeight", TypeF32, Offset(dustHeight, RigidShapeData), "Height of dust effects.\n");
  410. addField("dustTrailEmitter", TYPEID< ParticleEmitterData >(), Offset(dustTrailEmitter, RigidShapeData), "Particle emitter used to create a dust trail for the moving object.\n");
  411. addField("splashEmitter", TYPEID< ParticleEmitterData >(), Offset(splashEmitterList, RigidShapeData), VC_NUM_SPLASH_EMITTERS, "Array of pointers to ParticleEmitterData datablocks which will generate splash effects.\n");
  412. addField("splashFreqMod", TypeF32, Offset(splashFreqMod, RigidShapeData), "The simulated frequency modulation of a splash generated by this object. Multiplied along with speed and time elapsed when determining splash emition rate.\n");
  413. addField("splashVelEpsilon", TypeF32, Offset(splashVelEpsilon, RigidShapeData), "The threshold speed at which we consider the object's movement to have stopped when updating splash effects.\n");
  414. endGroup( "Particle Effects" );
  415. addGroup( "Sounds" );
  416. addField("softImpactSound", TypeSFXTrackName, Offset(body.sound[Body::SoftImpactSound], RigidShapeData),
  417. "Sound to play when body impacts with at least softImageSpeed but less than hardImpactSpeed." );
  418. addField("hardImpactSound", TypeSFXTrackName, Offset(body.sound[Body::HardImpactSound], RigidShapeData),
  419. "Sound to play when body impacts with at least hardImpactSpeed." );
  420. addField("exitSplashSoundVelocity", TypeF32, Offset(exitSplashSoundVel, RigidShapeData), "The minimum velocity at which the exit splash sound will be played when emerging from water.\n");
  421. addField("softSplashSoundVelocity", TypeF32, Offset(softSplashSoundVel, RigidShapeData),"The minimum velocity at which the soft splash sound will be played when impacting water.\n");
  422. addField("mediumSplashSoundVelocity", TypeF32, Offset(medSplashSoundVel, RigidShapeData), "The minimum velocity at which the medium splash sound will be played when impacting water.\n");
  423. addField("hardSplashSoundVelocity", TypeF32, Offset(hardSplashSoundVel, RigidShapeData), "The minimum velocity at which the hard splash sound will be played when impacting water.\n");
  424. addField("exitingWater", TypeSFXTrackName, Offset(waterSound[ExitWater], RigidShapeData), "The AudioProfile will be used to produce sounds when emerging from water.\n");
  425. addField("impactWaterEasy", TypeSFXTrackName, Offset(waterSound[ImpactSoft], RigidShapeData), "The AudioProfile will be used to produce sounds when a soft impact with water occurs.\n");
  426. addField("impactWaterMedium", TypeSFXTrackName, Offset(waterSound[ImpactMedium], RigidShapeData), "The AudioProfile will be used to produce sounds when a medium impact with water occurs.\n");
  427. addField("impactWaterHard", TypeSFXTrackName, Offset(waterSound[ImpactHard], RigidShapeData), "The AudioProfile will be used to produce sounds when a hard impact with water occurs.\n");
  428. addField("waterWakeSound", TypeSFXTrackName, Offset(waterSound[Wake], RigidShapeData), "The AudioProfile will be used to produce sounds when a water wake is displayed.\n");
  429. endGroup( "Sounds" );
  430. addGroup( "Camera" );
  431. addField("cameraRoll", TypeBool, Offset(cameraRoll, RigidShapeData), "Specifies whether the camera's rotation matrix, and the render eye transform are multiplied during camera updates.\n");
  432. addField("cameraLag", TypeF32, Offset(cameraLag, RigidShapeData), "Scalar amount by which the third person camera lags the object, relative to the object's linear velocity.\n");
  433. addField("cameraDecay", TypeF32, Offset(cameraDecay, RigidShapeData), "Scalar rate at which the third person camera offset decays, per tick.\n");
  434. addField("cameraOffset", TypeF32, Offset(cameraOffset, RigidShapeData), "The vertical offset of the object's camera.\n");
  435. endGroup( "Camera" );
  436. Parent::initPersistFields();
  437. }
  438. //----------------------------------------------------------------------------
  439. //----------------------------------------------------------------------------
  440. //----------------------------------------------------------------------------
  441. RigidShape::RigidShape()
  442. {
  443. mNetFlags.set(Ghostable);
  444. mDustTrailEmitter = NULL;
  445. mDataBlock = 0;
  446. // [rene, 27-Apr-11] WTH is a RigidShape a vehicle???
  447. mTypeMask |= VehicleObjectType | DynamicShapeObjectType;
  448. mDelta.pos = Point3F(0,0,0);
  449. mDelta.posVec = Point3F(0,0,0);
  450. mDelta.warpTicks = mDelta.warpCount = 0;
  451. mDelta.dt = 1;
  452. mDelta.move = NullMove;
  453. mPredictionCount = 0;
  454. mDelta.cameraOffset.set(0,0,0);
  455. mDelta.cameraVec.set(0,0,0);
  456. mDelta.cameraRot.set(0,0,0);
  457. mDelta.cameraRotVec.set(0,0,0);
  458. mRigid.linPosition.set(0, 0, 0);
  459. mRigid.linVelocity.set(0, 0, 0);
  460. mRigid.angPosition.identity();
  461. mRigid.angVelocity.set(0, 0, 0);
  462. mRigid.linMomentum.set(0, 0, 0);
  463. mRigid.angMomentum.set(0, 0, 0);
  464. mContacts.clear();
  465. mCameraOffset.set(0,0,0);
  466. dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) );
  467. dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) );
  468. mDisableMove = false; // start frozen by default
  469. restCount = 0;
  470. inLiquid = false;
  471. }
  472. RigidShape::~RigidShape()
  473. {
  474. //
  475. }
  476. U32 RigidShape::getCollisionMask()
  477. {
  478. if (isServerObject())
  479. return sServerCollisionMask;
  480. else
  481. return sClientCollisionMask;
  482. }
  483. Point3F RigidShape::getVelocity() const
  484. {
  485. return mRigid.linVelocity;
  486. }
  487. //----------------------------------------------------------------------------
  488. bool RigidShape::onAdd()
  489. {
  490. if (!Parent::onAdd())
  491. return false;
  492. // When loading from a mission script, the base SceneObject's transform
  493. // will have been set and needs to be transfered to the rigid body.
  494. mRigid.setTransform(mObjToWorld);
  495. // Initialize interpolation vars.
  496. mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
  497. mDelta.pos = mRigid.linPosition;
  498. mDelta.posVec = Point3F(0,0,0);
  499. // Create Emitters on the client
  500. if( isClientObject() )
  501. {
  502. if( mDataBlock->dustEmitter )
  503. {
  504. for( U32 i=0; i<RigidShapeData::VC_NUM_DUST_EMITTERS; i++ )
  505. {
  506. mDustEmitterList[i] = new ParticleEmitter;
  507. mDustEmitterList[i]->onNewDataBlock( mDataBlock->dustEmitter, false );
  508. if( !mDustEmitterList[i]->registerObject() )
  509. {
  510. Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
  511. delete mDustEmitterList[i];
  512. mDustEmitterList[i] = NULL;
  513. }
  514. }
  515. }
  516. for( U32 j=0; j<RigidShapeData::VC_NUM_SPLASH_EMITTERS; j++ )
  517. {
  518. if( mDataBlock->splashEmitterList[j] )
  519. {
  520. mSplashEmitterList[j] = new ParticleEmitter;
  521. mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j], false );
  522. if( !mSplashEmitterList[j]->registerObject() )
  523. {
  524. Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() );
  525. delete mSplashEmitterList[j];
  526. mSplashEmitterList[j] = NULL;
  527. }
  528. }
  529. }
  530. }
  531. // Create a new convex.
  532. AssertFatal(mDataBlock->collisionDetails[0] != -1, "Error, a rigid shape must have a collision-1 detail!");
  533. mConvex.mObject = this;
  534. mConvex.pShapeBase = this;
  535. mConvex.hullId = 0;
  536. mConvex.box = mObjBox;
  537. mConvex.box.minExtents.convolve(mObjScale);
  538. mConvex.box.maxExtents.convolve(mObjScale);
  539. mConvex.findNodeTransform();
  540. addToScene();
  541. if( !isServerObject() )
  542. {
  543. if( mDataBlock->dustTrailEmitter )
  544. {
  545. mDustTrailEmitter = new ParticleEmitter;
  546. mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false );
  547. if( !mDustTrailEmitter->registerObject() )
  548. {
  549. Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
  550. delete mDustTrailEmitter;
  551. mDustTrailEmitter = NULL;
  552. }
  553. }
  554. }
  555. if (isServerObject())
  556. scriptOnAdd();
  557. return true;
  558. }
  559. void RigidShape::onRemove()
  560. {
  561. scriptOnRemove();
  562. removeFromScene();
  563. U32 i=0;
  564. for( i=0; i<RigidShapeData::VC_NUM_DUST_EMITTERS; i++ )
  565. {
  566. if( mDustEmitterList[i] )
  567. {
  568. mDustEmitterList[i]->deleteWhenEmpty();
  569. mDustEmitterList[i] = NULL;
  570. }
  571. }
  572. for( i=0; i<RigidShapeData::VC_NUM_SPLASH_EMITTERS; i++ )
  573. {
  574. if( mSplashEmitterList[i] )
  575. {
  576. mSplashEmitterList[i]->deleteWhenEmpty();
  577. mSplashEmitterList[i] = NULL;
  578. }
  579. }
  580. Parent::onRemove();
  581. }
  582. //----------------------------------------------------------------------------
  583. void RigidShape::processTick(const Move* move)
  584. {
  585. Parent::processTick(move);
  586. if ( isMounted() )
  587. return;
  588. // Warp to catch up to server
  589. if (mDelta.warpCount < mDelta.warpTicks)
  590. {
  591. mDelta.warpCount++;
  592. // Set new pos.
  593. mObjToWorld.getColumn(3,&mDelta.pos);
  594. mDelta.pos += mDelta.warpOffset;
  595. mDelta.rot[0] = mDelta.rot[1];
  596. mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/mDelta.warpTicks);
  597. setPosition(mDelta.pos,mDelta.rot[1]);
  598. // Pos backstepping
  599. mDelta.posVec.x = -mDelta.warpOffset.x;
  600. mDelta.posVec.y = -mDelta.warpOffset.y;
  601. mDelta.posVec.z = -mDelta.warpOffset.z;
  602. }
  603. else
  604. {
  605. if (!move)
  606. {
  607. if (isGhost())
  608. {
  609. // If we haven't run out of prediction time,
  610. // predict using the last known move.
  611. if (mPredictionCount-- <= 0)
  612. return;
  613. move = &mDelta.move;
  614. }
  615. else
  616. move = &NullMove;
  617. }
  618. // Process input move
  619. updateMove(move);
  620. // Save current rigid state interpolation
  621. mDelta.posVec = mRigid.linPosition;
  622. mDelta.rot[0] = mRigid.angPosition;
  623. // Update the physics based on the integration rate
  624. S32 count = mDataBlock->integration;
  625. if (!mDisableMove)
  626. updateWorkingCollisionSet(getCollisionMask());
  627. for (U32 i = 0; i < count; i++)
  628. updatePos(TickSec / count);
  629. // Wrap up interpolation info
  630. mDelta.pos = mRigid.linPosition;
  631. mDelta.posVec -= mRigid.linPosition;
  632. mDelta.rot[1] = mRigid.angPosition;
  633. // Update container database
  634. setPosition(mRigid.linPosition, mRigid.angPosition);
  635. setMaskBits(PositionMask);
  636. updateContainer();
  637. }
  638. }
  639. void RigidShape::interpolateTick(F32 dt)
  640. {
  641. Parent::interpolateTick(dt);
  642. if ( isMounted() )
  643. return;
  644. if(dt == 0.0f)
  645. setRenderPosition(mDelta.pos, mDelta.rot[1]);
  646. else
  647. {
  648. QuatF rot;
  649. rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
  650. Point3F pos = mDelta.pos + mDelta.posVec * dt;
  651. setRenderPosition(pos,rot);
  652. }
  653. mDelta.dt = dt;
  654. }
  655. void RigidShape::advanceTime(F32 dt)
  656. {
  657. Parent::advanceTime(dt);
  658. updateFroth(dt);
  659. if ( isMounted() )
  660. return;
  661. // Update 3rd person camera offset. Camera update is done
  662. // here as it's a client side only animation.
  663. mCameraOffset -=
  664. (mCameraOffset * mDataBlock->cameraDecay +
  665. mRigid.linVelocity * mDataBlock->cameraLag) * dt;
  666. }
  667. //----------------------------------------------------------------------------
  668. bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload)
  669. {
  670. mDataBlock = dynamic_cast<RigidShapeData*>(dptr);
  671. if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
  672. return false;
  673. // Update Rigid Info
  674. mRigid.mass = mDataBlock->mass;
  675. mRigid.oneOverMass = 1 / mRigid.mass;
  676. mRigid.friction = mDataBlock->body.friction;
  677. mRigid.restitution = mDataBlock->body.restitution;
  678. mRigid.setCenterOfMass(mDataBlock->massCenter);
  679. // Ignores massBox, just set sphere for now. Derived objects
  680. // can set what they want.
  681. mRigid.setObjectInertia();
  682. scriptOnNewDataBlock();
  683. return true;
  684. }
  685. //----------------------------------------------------------------------------
  686. void RigidShape::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
  687. {
  688. *min = mDataBlock->cameraMinDist;
  689. *max = mDataBlock->cameraMaxDist;
  690. off->set(0,0,mDataBlock->cameraOffset);
  691. rot->identity();
  692. }
  693. //----------------------------------------------------------------------------
  694. void RigidShape::getCameraTransform(F32* pos,MatrixF* mat)
  695. {
  696. // Returns camera to world space transform
  697. // Handles first person / third person camera position
  698. if (isServerObject() && mShapeInstance)
  699. mShapeInstance->animateNodeSubtrees(true);
  700. if (*pos == 0)
  701. {
  702. getRenderEyeTransform(mat);
  703. return;
  704. }
  705. // Get the shape's camera parameters.
  706. F32 min,max;
  707. MatrixF rot;
  708. Point3F offset;
  709. getCameraParameters(&min,&max,&offset,&rot);
  710. // Start with the current eye position
  711. MatrixF eye;
  712. getRenderEyeTransform(&eye);
  713. // Build a transform that points along the eye axis
  714. // but where the Z axis is always up.
  715. if (mDataBlock->cameraRoll)
  716. mat->mul(eye,rot);
  717. else
  718. {
  719. MatrixF cam(1);
  720. VectorF x,y,z(0,0,1);
  721. eye.getColumn(1, &y);
  722. mCross(y, z, &x);
  723. x.normalize();
  724. mCross(x, y, &z);
  725. z.normalize();
  726. cam.setColumn(0,x);
  727. cam.setColumn(1,y);
  728. cam.setColumn(2,z);
  729. mat->mul(cam,rot);
  730. }
  731. // Camera is positioned straight back along the eye's -Y axis.
  732. // A ray is cast to make sure the camera doesn't go through
  733. // anything solid.
  734. VectorF vp,vec;
  735. vp.x = vp.z = 0;
  736. vp.y = -(max - min) * *pos;
  737. eye.mulV(vp,&vec);
  738. // Use the camera node as the starting position if it exists.
  739. Point3F osp,sp;
  740. if (mDataBlock->cameraNode != -1)
  741. {
  742. mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
  743. getRenderTransform().mulP(osp,&sp);
  744. }
  745. else
  746. eye.getColumn(3,&sp);
  747. // Make sure we don't hit ourself...
  748. disableCollision();
  749. if (isMounted())
  750. getObjectMount()->disableCollision();
  751. // Cast the ray into the container database to see if we're going
  752. // to hit anything.
  753. RayInfo collision;
  754. Point3F ep = sp + vec + offset + mCameraOffset;
  755. if (mContainer->castRay(sp, ep,
  756. ~(WaterObjectType | GameBaseObjectType | DefaultObjectType),
  757. &collision) == true)
  758. {
  759. // Shift the collision point back a little to try and
  760. // avoid clipping against the front camera plane.
  761. F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1;
  762. if (t > 0.0f)
  763. ep = sp + offset + mCameraOffset + (vec * t);
  764. else
  765. eye.getColumn(3,&ep);
  766. }
  767. mat->setColumn(3,ep);
  768. // Re-enable our collision.
  769. if (isMounted())
  770. getObjectMount()->enableCollision();
  771. enableCollision();
  772. // Apply Camera FX.
  773. mat->mul( gCamFXMgr.getTrans() );
  774. }
  775. //----------------------------------------------------------------------------
  776. void RigidShape::getVelocity(const Point3F& r, Point3F* v)
  777. {
  778. mRigid.getVelocity(r, v);
  779. }
  780. void RigidShape::applyImpulse(const Point3F &pos, const Point3F &impulse)
  781. {
  782. Point3F r;
  783. mRigid.getOriginVector(pos,&r);
  784. mRigid.applyImpulse(r, impulse);
  785. }
  786. //----------------------------------------------------------------------------
  787. void RigidShape::updateMove(const Move* move)
  788. {
  789. mDelta.move = *move;
  790. }
  791. //----------------------------------------------------------------------------
  792. void RigidShape::setPosition(const Point3F& pos,const QuatF& rot)
  793. {
  794. MatrixF mat;
  795. rot.setMatrix(&mat);
  796. mat.setColumn(3,pos);
  797. Parent::setTransform(mat);
  798. }
  799. void RigidShape::setRenderPosition(const Point3F& pos, const QuatF& rot)
  800. {
  801. MatrixF mat;
  802. rot.setMatrix(&mat);
  803. mat.setColumn(3,pos);
  804. Parent::setRenderTransform(mat);
  805. }
  806. void RigidShape::setTransform(const MatrixF& newMat)
  807. {
  808. mRigid.setTransform(newMat);
  809. Parent::setTransform(newMat);
  810. mRigid.atRest = false;
  811. mContacts.clear();
  812. }
  813. void RigidShape::forceClientTransform()
  814. {
  815. setMaskBits(ForceMoveMask);
  816. }
  817. //-----------------------------------------------------------------------------
  818. void RigidShape::disableCollision()
  819. {
  820. Parent::disableCollision();
  821. }
  822. void RigidShape::enableCollision()
  823. {
  824. Parent::enableCollision();
  825. }
  826. //----------------------------------------------------------------------------
  827. /** Update the physics
  828. */
  829. void RigidShape::updatePos(F32 dt)
  830. {
  831. Point3F origVelocity = mRigid.linVelocity;
  832. // Update internal forces acting on the body.
  833. mRigid.clearForces();
  834. updateForces(dt);
  835. // Update collision information based on our current pos.
  836. bool collided = false;
  837. if (!mRigid.atRest && !mDisableMove)
  838. {
  839. collided = updateCollision(dt);
  840. // Now that all the forces have been processed, lets
  841. // see if we're at rest. Basically, if the kinetic energy of
  842. // the shape is less than some percentage of the energy added
  843. // by gravity for a short period, we're considered at rest.
  844. // This should really be part of the rigid class...
  845. if (mCollisionList.getCount())
  846. {
  847. F32 k = mRigid.getKineticEnergy();
  848. F32 G = sRigidShapeGravity * dt;
  849. F32 Kg = 0.5 * mRigid.mass * G * G;
  850. if (k < sRestTol * Kg && ++restCount > sRestCount)
  851. mRigid.setAtRest();
  852. }
  853. else
  854. restCount = 0;
  855. }
  856. // Integrate forward
  857. if (!mRigid.atRest && !mDisableMove)
  858. mRigid.integrate(dt);
  859. // Deal with client and server scripting, sounds, etc.
  860. if (isServerObject())
  861. {
  862. // Check triggers and other objects that we normally don't
  863. // collide with. This function must be called before notifyCollision
  864. // as it will queue collision.
  865. checkTriggers();
  866. // Invoke the onCollision notify callback for all the objects
  867. // we've just hit.
  868. notifyCollision();
  869. // Server side impact script callback
  870. if (collided)
  871. {
  872. VectorF collVec = mRigid.linVelocity - origVelocity;
  873. F32 collSpeed = collVec.len();
  874. if (collSpeed > mDataBlock->minImpactSpeed)
  875. onImpact(collVec);
  876. }
  877. // Water script callbacks
  878. if (!inLiquid && mWaterCoverage != 0.0f)
  879. {
  880. onEnterLiquid_callback(getIdString(), mWaterCoverage, mLiquidType.c_str() );
  881. inLiquid = true;
  882. }
  883. else if (inLiquid && mWaterCoverage == 0.0f)
  884. {
  885. onLeaveLiquid_callback(getIdString(), mLiquidType.c_str() );
  886. inLiquid = false;
  887. }
  888. }
  889. else {
  890. // Play impact sounds on the client.
  891. if (collided) {
  892. F32 collSpeed = (mRigid.linVelocity - origVelocity).len();
  893. S32 impactSound = -1;
  894. if (collSpeed >= mDataBlock->hardImpactSpeed)
  895. impactSound = RigidShapeData::Body::HardImpactSound;
  896. else
  897. if (collSpeed >= mDataBlock->softImpactSpeed)
  898. impactSound = RigidShapeData::Body::SoftImpactSound;
  899. if (impactSound != -1 && mDataBlock->body.sound[impactSound] != NULL)
  900. SFX->playOnce(mDataBlock->body.sound[impactSound], &getTransform());
  901. }
  902. // Water volume sounds
  903. F32 vSpeed = getVelocity().len();
  904. if (!inLiquid && mWaterCoverage >= 0.8f) {
  905. if (vSpeed >= mDataBlock->hardSplashSoundVel)
  906. SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactHard], &getTransform());
  907. else
  908. if (vSpeed >= mDataBlock->medSplashSoundVel)
  909. SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactMedium], &getTransform());
  910. else
  911. if (vSpeed >= mDataBlock->softSplashSoundVel)
  912. SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactSoft], &getTransform());
  913. inLiquid = true;
  914. }
  915. else
  916. if(inLiquid && mWaterCoverage < 0.8f) {
  917. if (vSpeed >= mDataBlock->exitSplashSoundVel)
  918. SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ExitWater], &getTransform());
  919. inLiquid = false;
  920. }
  921. }
  922. }
  923. //----------------------------------------------------------------------------
  924. void RigidShape::updateForces(F32 /*dt*/)
  925. {
  926. if (mDisableMove) return;
  927. Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod);
  928. MatrixF currTransform;
  929. mRigid.getTransform(&currTransform);
  930. Point3F torque(0, 0, 0);
  931. Point3F force(0, 0, 0);
  932. Point3F vel = mRigid.linVelocity;
  933. // Gravity
  934. force += gravForce;
  935. // Apply drag
  936. Point3F vDrag = mRigid.linVelocity;
  937. vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
  938. force -= vDrag * mDataBlock->dragForce;
  939. // Add in physical zone force
  940. force += mAppliedForce;
  941. // Container buoyancy & drag
  942. force += Point3F(0, 0,-mBuoyancy * sRigidShapeGravity * mRigid.mass * mGravityMod);
  943. force -= mRigid.linVelocity * mDrag;
  944. torque -= mRigid.angMomentum * mDrag;
  945. mRigid.force = force;
  946. mRigid.torque = torque;
  947. }
  948. //-----------------------------------------------------------------------------
  949. /** Update collision information
  950. Update the convex state and check for collisions. If the object is in
  951. collision, impact and contact forces are generated.
  952. */
  953. bool RigidShape::updateCollision(F32 dt)
  954. {
  955. // Update collision information
  956. MatrixF mat,cmat;
  957. mConvex.transform = &mat;
  958. mRigid.getTransform(&mat);
  959. cmat = mConvex.getTransform();
  960. mCollisionList.clear();
  961. CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol);
  962. if (state && state->mDist <= mDataBlock->collisionTol)
  963. {
  964. //resolveDisplacement(ns,state,dt);
  965. mConvex.getCollisionInfo(cmat, getScale(), &mCollisionList, mDataBlock->collisionTol);
  966. }
  967. // Resolve collisions
  968. bool collided = resolveCollision(mRigid,mCollisionList);
  969. resolveContacts(mRigid,mCollisionList,dt);
  970. return collided;
  971. }
  972. //----------------------------------------------------------------------------
  973. /** Resolve collision impacts
  974. Handle collision impacts, as opposed to contacts. Impulses are calculated based
  975. on standard collision resolution formulas.
  976. */
  977. bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList)
  978. {
  979. // Apply impulses to resolve collision
  980. bool colliding, collided = false;
  981. do
  982. {
  983. colliding = false;
  984. for (S32 i = 0; i < cList.getCount(); i++)
  985. {
  986. Collision& c = cList[i];
  987. if (c.distance < mDataBlock->collisionTol)
  988. {
  989. // Velocity into surface
  990. Point3F v,r;
  991. ns.getOriginVector(c.point,&r);
  992. ns.getVelocity(r,&v);
  993. F32 vn = mDot(v,c.normal);
  994. // Only interested in velocities greater than sContactTol,
  995. // velocities less than that will be dealt with as contacts
  996. // "constraints".
  997. if (vn < -mDataBlock->contactTol)
  998. {
  999. // Apply impulses to the rigid body to keep it from
  1000. // penetrating the surface.
  1001. ns.resolveCollision(cList[i].point,
  1002. cList[i].normal);
  1003. colliding = collided = true;
  1004. // Keep track of objects we collide with
  1005. if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType)
  1006. {
  1007. ShapeBase* col = static_cast<ShapeBase*>(c.object);
  1008. queueCollision(col,v - col->getVelocity());
  1009. }
  1010. }
  1011. }
  1012. }
  1013. } while (colliding);
  1014. return collided;
  1015. }
  1016. //----------------------------------------------------------------------------
  1017. /** Resolve contact forces
  1018. Resolve contact forces using the "penalty" method. Forces are generated based
  1019. on the depth of penetration and the moment of inertia at the point of contact.
  1020. */
  1021. bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
  1022. {
  1023. // Use spring forces to manage contact constraints.
  1024. bool collided = false;
  1025. Point3F t,p(0,0,0),l(0,0,0);
  1026. for (S32 i = 0; i < cList.getCount(); i++)
  1027. {
  1028. Collision& c = cList[i];
  1029. if (c.distance < mDataBlock->collisionTol)
  1030. {
  1031. // Velocity into the surface
  1032. Point3F v,r;
  1033. ns.getOriginVector(c.point,&r);
  1034. ns.getVelocity(r,&v);
  1035. F32 vn = mDot(v,c.normal);
  1036. // Only interested in velocities less than mDataBlock->contactTol,
  1037. // velocities greater than that are dealt with as collisions.
  1038. if (mFabs(vn) < mDataBlock->contactTol)
  1039. {
  1040. collided = true;
  1041. // Penetration force. This is actually a spring which
  1042. // will seperate the body from the collision surface.
  1043. F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal));
  1044. F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi);
  1045. Point3F f = c.normal * s;
  1046. // Friction impulse, calculated as a function of the
  1047. // amount of force it would take to stop the motion
  1048. // perpendicular to the normal.
  1049. Point3F uv = v - (c.normal * vn);
  1050. F32 ul = uv.len();
  1051. if (s > 0 && ul)
  1052. {
  1053. uv /= -ul;
  1054. F32 u = ul * ns.getZeroImpulse(r,uv);
  1055. s *= mRigid.friction;
  1056. if (u > s)
  1057. u = s;
  1058. f += uv * u;
  1059. }
  1060. // Accumulate forces
  1061. p += f;
  1062. mCross(r,f,&t);
  1063. l += t;
  1064. }
  1065. }
  1066. }
  1067. // Contact constraint forces act over time...
  1068. ns.linMomentum += p * dt;
  1069. ns.angMomentum += l * dt;
  1070. ns.updateVelocity();
  1071. return true;
  1072. }
  1073. //----------------------------------------------------------------------------
  1074. bool RigidShape::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt)
  1075. {
  1076. SceneObject* obj = (state->mA->getObject() == this)?
  1077. state->mB->getObject(): state->mA->getObject();
  1078. if (obj->isDisplacable() && ((obj->getTypeMask() & ShapeBaseObjectType) != 0))
  1079. {
  1080. // Try to displace the object by the amount we're trying to move
  1081. Point3F objNewMom = ns.linVelocity * obj->getMass() * 1.1f;
  1082. Point3F objOldMom = obj->getMomentum();
  1083. Point3F objNewVel = objNewMom / obj->getMass();
  1084. Point3F myCenter;
  1085. Point3F theirCenter;
  1086. getWorldBox().getCenter(&myCenter);
  1087. obj->getWorldBox().getCenter(&theirCenter);
  1088. if (mDot(myCenter - theirCenter, objNewMom) >= 0.0f || objNewVel.len() < 0.01)
  1089. {
  1090. objNewMom = (theirCenter - myCenter);
  1091. objNewMom.normalize();
  1092. objNewMom *= 1.0f * obj->getMass();
  1093. objNewVel = objNewMom / obj->getMass();
  1094. }
  1095. obj->setMomentum(objNewMom);
  1096. if (obj->displaceObject(objNewVel * 1.1f * dt) == true)
  1097. {
  1098. // Queue collision and change in velocity
  1099. VectorF dv = (objOldMom - objNewMom) / obj->getMass();
  1100. queueCollision(static_cast<ShapeBase*>(obj), dv);
  1101. return true;
  1102. }
  1103. }
  1104. return false;
  1105. }
  1106. //----------------------------------------------------------------------------
  1107. void RigidShape::updateWorkingCollisionSet(const U32 mask)
  1108. {
  1109. Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
  1110. F32 len = (mRigid.linVelocity.len() + 50) * TickSec;
  1111. F32 l = (len * 1.1) + 0.1; // fudge factor
  1112. convexBox.minExtents -= Point3F(l, l, l);
  1113. convexBox.maxExtents += Point3F(l, l, l);
  1114. disableCollision();
  1115. mConvex.updateWorkingList(convexBox, mask);
  1116. enableCollision();
  1117. }
  1118. //----------------------------------------------------------------------------
  1119. /** Check collisions with trigger and items
  1120. Perform a container search using the current bounding box
  1121. of the main body, wheels are not included. This method should
  1122. only be called on the server.
  1123. */
  1124. void RigidShape::checkTriggers()
  1125. {
  1126. Box3F bbox = mConvex.getBoundingBox(getTransform(), getScale());
  1127. gServerContainer.findObjects(bbox,sTriggerMask,findCallback,this);
  1128. }
  1129. /** The callback used in by the checkTriggers() method.
  1130. The checkTriggers method uses a container search which will
  1131. invoke this callback on each obj that matches.
  1132. */
  1133. void RigidShape::findCallback(SceneObject* obj,void *key)
  1134. {
  1135. RigidShape* shape = reinterpret_cast<RigidShape*>(key);
  1136. U32 objectMask = obj->getTypeMask();
  1137. // Check: triggers, corpses and items, basically the same things
  1138. // that the player class checks for
  1139. if (objectMask & TriggerObjectType) {
  1140. Trigger* pTrigger = static_cast<Trigger*>(obj);
  1141. pTrigger->potentialEnterObject(shape);
  1142. }
  1143. else if (objectMask & CorpseObjectType) {
  1144. ShapeBase* col = static_cast<ShapeBase*>(obj);
  1145. shape->queueCollision(col,shape->getVelocity() - col->getVelocity());
  1146. }
  1147. else if (objectMask & ItemObjectType) {
  1148. Item* item = static_cast<Item*>(obj);
  1149. if (shape != item->getCollisionObject())
  1150. shape->queueCollision(item,shape->getVelocity() - item->getVelocity());
  1151. }
  1152. }
  1153. //----------------------------------------------------------------------------
  1154. void RigidShape::writePacketData(GameConnection *connection, BitStream *stream)
  1155. {
  1156. Parent::writePacketData(connection, stream);
  1157. mathWrite(*stream, mRigid.linPosition);
  1158. mathWrite(*stream, mRigid.angPosition);
  1159. mathWrite(*stream, mRigid.linMomentum);
  1160. mathWrite(*stream, mRigid.angMomentum);
  1161. stream->writeFlag(mRigid.atRest);
  1162. stream->writeFlag(mContacts.getCount() == 0);
  1163. stream->writeFlag(mDisableMove);
  1164. stream->setCompressionPoint(mRigid.linPosition);
  1165. }
  1166. void RigidShape::readPacketData(GameConnection *connection, BitStream *stream)
  1167. {
  1168. Parent::readPacketData(connection, stream);
  1169. mathRead(*stream, &mRigid.linPosition);
  1170. mathRead(*stream, &mRigid.angPosition);
  1171. mathRead(*stream, &mRigid.linMomentum);
  1172. mathRead(*stream, &mRigid.angMomentum);
  1173. mRigid.atRest = stream->readFlag();
  1174. if (stream->readFlag())
  1175. mContacts.clear();
  1176. mRigid.updateInertialTensor();
  1177. mRigid.updateVelocity();
  1178. mDisableMove = stream->readFlag();
  1179. stream->setCompressionPoint(mRigid.linPosition);
  1180. }
  1181. //----------------------------------------------------------------------------
  1182. U32 RigidShape::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
  1183. {
  1184. U32 retMask = Parent::packUpdate(con, mask, stream);
  1185. // The rest of the data is part of the control object packet update.
  1186. // If we're controlled by this client, we don't need to send it.
  1187. if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
  1188. return retMask;
  1189. mDelta.move.pack(stream);
  1190. if (stream->writeFlag(mask & PositionMask))
  1191. {
  1192. stream->writeFlag(mask & ForceMoveMask);
  1193. stream->writeCompressedPoint(mRigid.linPosition);
  1194. mathWrite(*stream, mRigid.angPosition);
  1195. mathWrite(*stream, mRigid.linMomentum);
  1196. mathWrite(*stream, mRigid.angMomentum);
  1197. stream->writeFlag(mRigid.atRest);
  1198. }
  1199. if(stream->writeFlag(mask & FreezeMask))
  1200. stream->writeFlag(mDisableMove);
  1201. return retMask;
  1202. }
  1203. void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream)
  1204. {
  1205. Parent::unpackUpdate(con,stream);
  1206. if (stream->readFlag())
  1207. return;
  1208. mDelta.move.unpack(stream);
  1209. if (stream->readFlag())
  1210. {
  1211. // Check if we need to jump to the given transform
  1212. // rather than interpolate to it.
  1213. bool forceUpdate = stream->readFlag();
  1214. mPredictionCount = sMaxPredictionTicks;
  1215. F32 speed = mRigid.linVelocity.len();
  1216. mDelta.warpRot[0] = mRigid.angPosition;
  1217. // Read in new position and momentum values
  1218. stream->readCompressedPoint(&mRigid.linPosition);
  1219. mathRead(*stream, &mRigid.angPosition);
  1220. mathRead(*stream, &mRigid.linMomentum);
  1221. mathRead(*stream, &mRigid.angMomentum);
  1222. mRigid.atRest = stream->readFlag();
  1223. mRigid.updateVelocity();
  1224. if (!forceUpdate && isProperlyAdded())
  1225. {
  1226. // Determine number of ticks to warp based on the average
  1227. // of the client and server velocities.
  1228. Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
  1229. mDelta.warpOffset = mRigid.linPosition - cp;
  1230. // Calc the distance covered in one tick as the average of
  1231. // the old speed and the new speed from the server.
  1232. F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec;
  1233. // Cal how many ticks it will take to cover the warp offset.
  1234. // If it's less than what's left in the current tick, we'll just
  1235. // warp in the remaining time.
  1236. if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
  1237. dt = mDelta.dt + sMaxWarpTicks;
  1238. else
  1239. dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
  1240. // Adjust current frame interpolation
  1241. if (mDelta.dt)
  1242. {
  1243. mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
  1244. mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
  1245. QuatF cr;
  1246. cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt);
  1247. mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt);
  1248. mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt);
  1249. }
  1250. // Calculated multi-tick warp
  1251. mDelta.warpCount = 0;
  1252. mDelta.warpTicks = (S32)(mFloor(dt));
  1253. if (mDelta.warpTicks)
  1254. {
  1255. mDelta.warpOffset = mRigid.linPosition - mDelta.pos;
  1256. mDelta.warpOffset /= mDelta.warpTicks;
  1257. mDelta.warpRot[0] = mDelta.rot[1];
  1258. mDelta.warpRot[1] = mRigid.angPosition;
  1259. }
  1260. }
  1261. else
  1262. {
  1263. // Set the shape to the server position
  1264. mDelta.dt = 0;
  1265. mDelta.pos = mRigid.linPosition;
  1266. mDelta.posVec.set(0,0,0);
  1267. mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
  1268. mDelta.warpCount = mDelta.warpTicks = 0;
  1269. setPosition(mRigid.linPosition, mRigid.angPosition);
  1270. }
  1271. }
  1272. if(stream->readFlag())
  1273. mDisableMove = stream->readFlag();
  1274. }
  1275. //----------------------------------------------------------------------------
  1276. void RigidShape::initPersistFields()
  1277. {
  1278. Parent::initPersistFields();
  1279. }
  1280. //----------------------------------------------------------------------------
  1281. void RigidShape::updateLiftoffDust( F32 dt )
  1282. {
  1283. Point3F offset( 0.0, 0.0, mDataBlock->dustHeight );
  1284. emitDust( mDustEmitterList[ 0 ], mDataBlock->triggerDustHeight, offset,
  1285. ( U32 )( dt * 1000 ) );
  1286. }
  1287. //--------------------------------------------------------------------------
  1288. void RigidShape::updateFroth( F32 dt )
  1289. {
  1290. // update bubbles
  1291. Point3F moveDir = getVelocity();
  1292. Point3F contactPoint;
  1293. F32 speed = moveDir.len();
  1294. if( speed < mDataBlock->splashVelEpsilon ) speed = 0.0;
  1295. U32 emitRate = (U32)(speed * mDataBlock->splashFreqMod * dt);
  1296. U32 i;
  1297. for( i=0; i<RigidShapeData::VC_NUM_SPLASH_EMITTERS; i++ )
  1298. {
  1299. if( mSplashEmitterList[i] )
  1300. {
  1301. mSplashEmitterList[i]->emitParticles( contactPoint, contactPoint, Point3F( 0.0, 0.0, 1.0 ),
  1302. moveDir, emitRate );
  1303. }
  1304. }
  1305. }
  1306. //--------------------------------------------------------------------------
  1307. // Returns true if shape is intersecting a water surface (roughly)
  1308. //--------------------------------------------------------------------------
  1309. bool RigidShape::collidingWithWater( Point3F &waterHeight )
  1310. {
  1311. Point3F curPos = getPosition();
  1312. F32 height = mFabs( mObjBox.maxExtents.z - mObjBox.minExtents.z );
  1313. RayInfo rInfo;
  1314. if( gClientContainer.castRay( curPos + Point3F(0.0, 0.0, height), curPos, WaterObjectType, &rInfo) )
  1315. {
  1316. waterHeight = rInfo.point;
  1317. return true;
  1318. }
  1319. return false;
  1320. }
  1321. void RigidShape::setEnergyLevel(F32 energy)
  1322. {
  1323. Parent::setEnergyLevel(energy);
  1324. setMaskBits(EnergyMask);
  1325. }
  1326. void RigidShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex )
  1327. {
  1328. Parent::prepBatchRender( state, mountedImageIndex );
  1329. if ( !gShowBoundingBox )
  1330. return;
  1331. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  1332. ri->renderDelegate.bind( this, &RigidShape::_renderMassAndContacts );
  1333. ri->type = RenderPassManager::RIT_Editor;
  1334. state->getRenderPass()->addInst( ri );
  1335. }
  1336. void RigidShape::_renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
  1337. {
  1338. // Box for the center of Mass
  1339. GFXStateBlockDesc desc;
  1340. desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  1341. desc.setZReadWrite(false);
  1342. desc.fillMode = GFXFillWireframe;
  1343. GFX->getDrawUtil()->drawCube( desc, Point3F(0.1f,0.1f,0.1f), mDataBlock->massCenter, ColorI(255, 255, 255), &mRenderObjToWorld );
  1344. // Collision points...
  1345. for (S32 i = 0; i < mCollisionList.getCount(); i++)
  1346. {
  1347. const Collision& collision = mCollisionList[i];
  1348. GFX->getDrawUtil()->drawCube( desc, Point3F(0.05f,0.05f,0.05f), collision.point, ColorI(0, 0, 255) );
  1349. }
  1350. // Render the normals as one big batch...
  1351. PrimBuild::begin(GFXLineList, mCollisionList.getCount() * 2);
  1352. for (S32 i = 0; i < mCollisionList.getCount(); i++)
  1353. {
  1354. const Collision& collision = mCollisionList[i];
  1355. PrimBuild::color3f(1, 1, 1);
  1356. PrimBuild::vertex3fv(collision.point);
  1357. PrimBuild::vertex3fv(collision.point + collision.normal * 0.05f);
  1358. }
  1359. PrimBuild::end();
  1360. // Build and render the collision polylist which is returned
  1361. // in the server's world space.
  1362. ClippedPolyList polyList;
  1363. polyList.mPlaneList.setSize(6);
  1364. polyList.mPlaneList[0].set(getWorldBox().minExtents,VectorF(-1,0,0));
  1365. polyList.mPlaneList[1].set(getWorldBox().minExtents,VectorF(0,-1,0));
  1366. polyList.mPlaneList[2].set(getWorldBox().minExtents,VectorF(0,0,-1));
  1367. polyList.mPlaneList[3].set(getWorldBox().maxExtents,VectorF(1,0,0));
  1368. polyList.mPlaneList[4].set(getWorldBox().maxExtents,VectorF(0,1,0));
  1369. polyList.mPlaneList[5].set(getWorldBox().maxExtents,VectorF(0,0,1));
  1370. Box3F dummyBox;
  1371. SphereF dummySphere;
  1372. buildPolyList(PLC_Collision, &polyList, dummyBox, dummySphere);
  1373. //polyList.render();
  1374. }
  1375. void RigidShape::reset()
  1376. {
  1377. mRigid.clearForces();
  1378. mRigid.setAtRest();
  1379. }
  1380. void RigidShape::freezeSim(bool frozen)
  1381. {
  1382. mDisableMove = frozen;
  1383. setMaskBits(FreezeMask);
  1384. }
  1385. DefineEngineMethod( RigidShape, reset, void, (),,
  1386. "@brief Clears physic forces from the shape and sets it at rest.\n\n"
  1387. "@tsexample\n"
  1388. "// Inform the RigidShape object to reset.\n"
  1389. "%thisRigidShape.reset();\n"
  1390. "@endtsexample\n\n"
  1391. "@see ShapeBaseData")
  1392. {
  1393. object->reset();
  1394. }
  1395. DefineEngineMethod( RigidShape, freezeSim, void, (bool isFrozen),,
  1396. "@brief Enables or disables the physics simulation on the RigidShape object.\n\n"
  1397. "@param isFrozen Boolean frozen state to set the object.\n"
  1398. "@tsexample\n"
  1399. "// Define the frozen state.\n"
  1400. "%isFrozen = \"true\";\n\n"
  1401. "// Inform the object of the defined frozen state\n"
  1402. "%thisRigidShape.freezeSim(%isFrozen);\n"
  1403. "@endtsexample\n\n"
  1404. "@see ShapeBaseData")
  1405. {
  1406. object->freezeSim(isFrozen);
  1407. }
  1408. DefineEngineMethod( RigidShape, forceClientTransform, void, (),,
  1409. "@brief Forces the client to jump to the RigidShape's transform rather then warp to it.\n\n")
  1410. {
  1411. if(object->isServerObject())
  1412. {
  1413. object->forceClientTransform();
  1414. }
  1415. }