rbody.cpp 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWPhys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/rbody.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 3/28/02 11:00a $*
  29. * *
  30. * $Revision:: 119 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * RigidBodyClass::RigidBodyClass -- Constructor for RigidBodyClass *
  35. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  36. #include "rbody.h"
  37. #include "pscene.h"
  38. #include "boxrobj.h"
  39. #include "physcoltest.h"
  40. #include "physinttest.h"
  41. #include "physcon.h"
  42. #include "octbox.h"
  43. #include "bitstream.h"
  44. #include "persistfactory.h"
  45. #include "simpledefinitionfactory.h"
  46. #include "wwphysids.h"
  47. #include "wwhack.h"
  48. #include "wwprofile.h"
  49. #include "hlod.h"
  50. #include "physcontrol.h"
  51. #include "phys3.h"
  52. #include <stdio.h>
  53. DECLARE_FORCE_LINK(rbody);
  54. #define RBODY_DEBUGGING 0
  55. #define RBODY_DEBUG_FILTER (stricmp(Model->Get_Name(),"V_GDI_ORCA_M") == 0) && (PhysicsSceneClass::Get_Instance()->Is_Debug_Display_Enabled())
  56. #define JITTER_ELIMINATION_CODE 0
  57. float RigidBodyClass::_CorrectionTime = 1.0f;
  58. /***********************************************************************************************
  59. **
  60. ** RBodyHistoryClass Implementation
  61. **
  62. ***********************************************************************************************/
  63. /*
  64. ** RBodyHistoryClass Parameters
  65. ** Control the implementation of the phys3 history tracking system with the following
  66. ** parameters.
  67. */
  68. const int RBODY_SNAPSHOT_COUNT = 16; // must be power of 2!
  69. const float RBODY_HISTORY_MIN_TIME = 0.75f; // seconds of history to store
  70. const int RBODY_SNAPSHOT_MASK = RBODY_SNAPSHOT_COUNT - 1;
  71. const float RBODY_SNAPSHOT_INTERVAL = RBODY_HISTORY_MIN_TIME / RBODY_SNAPSHOT_COUNT;
  72. #define RBODYHISTORY_NO_CORRECTION 0
  73. #define RBODYHISTORY_ABSOLUTE_CORRECTION 0
  74. #define RBODYHISTORY_LERP_CORRECTION 1
  75. /**
  76. ** RBodyHistoryClass
  77. ** This class is used to store a history of the state of a RBody object. The network
  78. ** update code uses this history to intelligently update the state of the object when
  79. ** a packet is received.
  80. */
  81. class RBodyHistoryClass
  82. {
  83. public:
  84. RBodyHistoryClass(void);
  85. ~RBodyHistoryClass(void);
  86. void Init(const RigidBodyStateStruct & state);
  87. void Update_History(const RigidBodyStateStruct & state, float dt);
  88. void Compute_Old_State(float dt,RigidBodyStateStruct * set_state);
  89. void Apply_Correction(const RigidBodyStateStruct & error,float fraction);
  90. int History_Count(void) { return RBODY_SNAPSHOT_COUNT; }
  91. const RigidBodyStateStruct & Get_Historical_State(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)]; }
  92. const Vector3 & Get_Historical_Position(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)].Position; }
  93. void Find_Nearest_State(const RigidBodyStateStruct & input,RigidBodyStateStruct * output);
  94. private:
  95. int Wrap_Index(int index) { return (index + RBODY_SNAPSHOT_COUNT) & RBODY_SNAPSHOT_MASK; }
  96. struct StateSnapshotStruct : public RigidBodyStateStruct
  97. {
  98. public:
  99. StateSnapshotStruct(void) : Age(0) { }
  100. StateSnapshotStruct(const StateSnapshotStruct & that) : RigidBodyStateStruct(that) { Age = that.Age; }
  101. StateSnapshotStruct & operator = (const StateSnapshotStruct & that) { RigidBodyStateStruct::operator = (that); Age = that.Age; return *this; }
  102. StateSnapshotStruct & operator = (const RigidBodyStateStruct & that) { RigidBodyStateStruct::operator = (that); Age = 0.0f; return *this; }
  103. void Lerp(const StateSnapshotStruct & a, const StateSnapshotStruct & b, float fraction);
  104. void Update_Age(float dt) { Age += dt; }
  105. float Age;
  106. };
  107. StateSnapshotStruct * SnapshotArray;
  108. int HeadIndex; // history buffer is circular, this is the "head"
  109. };
  110. RBodyHistoryClass::RBodyHistoryClass(void) :
  111. SnapshotArray(NULL),
  112. HeadIndex(0)
  113. {
  114. SnapshotArray = new StateSnapshotStruct[RBODY_SNAPSHOT_COUNT];
  115. }
  116. RBodyHistoryClass::~RBodyHistoryClass(void)
  117. {
  118. if (SnapshotArray != NULL) {
  119. delete[] SnapshotArray;
  120. SnapshotArray = NULL;
  121. }
  122. }
  123. void RBodyHistoryClass::Init(const RigidBodyStateStruct & state)
  124. {
  125. int next_older_index = Wrap_Index(HeadIndex + 1);
  126. SnapshotArray[HeadIndex] = state;
  127. SnapshotArray[HeadIndex].Age = 0.0f;
  128. SnapshotArray[next_older_index] = state;
  129. SnapshotArray[next_older_index].Age = 1000.0f;
  130. }
  131. void RBodyHistoryClass::Update_History(const RigidBodyStateStruct & state, float dt)
  132. {
  133. int next_older_index = Wrap_Index(HeadIndex + 1);
  134. /*
  135. ** See if enough time has passed so we need to ratchet forward in the circular buffer
  136. */
  137. if (SnapshotArray[next_older_index].Age + dt > RBODY_SNAPSHOT_INTERVAL) {
  138. HeadIndex = Wrap_Index(HeadIndex - 1);
  139. SnapshotArray[HeadIndex].Age = 0;
  140. }
  141. SnapshotArray[HeadIndex] = state;
  142. /*
  143. ** Add age to all existing snapshots
  144. */
  145. for (int i=0; i<RBODY_SNAPSHOT_COUNT; i++) {
  146. if (i != HeadIndex) {
  147. SnapshotArray[i].Update_Age(dt);
  148. }
  149. }
  150. }
  151. void RBodyHistoryClass::Compute_Old_State(float t,RigidBodyStateStruct * set_state)
  152. {
  153. WWASSERT(set_state != NULL);
  154. int index = HeadIndex;
  155. bool done = false;
  156. while (!done) {
  157. if (SnapshotArray[index].Age <= t) {
  158. index = Wrap_Index(index + 1);
  159. /*
  160. ** past the end of our history, just return the oldest known state
  161. */
  162. if (index == HeadIndex) {
  163. int tail_index = Wrap_Index(HeadIndex - 1);
  164. *set_state = SnapshotArray[tail_index];
  165. return;
  166. }
  167. } else {
  168. done = true;
  169. }
  170. }
  171. int prev_index = Wrap_Index(index - 1);
  172. int next_index = index;
  173. float fraction = (t - SnapshotArray[prev_index].Age) / (SnapshotArray[next_index].Age - SnapshotArray[prev_index].Age);
  174. RigidBodyStateStruct::Lerp(SnapshotArray[prev_index],SnapshotArray[next_index],fraction,set_state);
  175. }
  176. void RBodyHistoryClass::Apply_Correction(const RigidBodyStateStruct & error,float fraction)
  177. {
  178. #if (!RBODY_HISTORY_NO_CORRECTION)
  179. for (int counter=0; counter<RBODY_SNAPSHOT_COUNT; counter++) {
  180. int index = Wrap_Index(HeadIndex + counter);
  181. RigidBodyStateStruct ideal_state = SnapshotArray[index];
  182. ideal_state.Position += error.Position;
  183. ideal_state.Orientation = ideal_state.Orientation * error.Orientation;
  184. ideal_state.LMomentum += error.LMomentum;
  185. ideal_state.AMomentum += error.AMomentum;
  186. float lerp_fraction = 0.0f;
  187. #if RBODYHISTORY_ABSOLUTE_CORRECTION
  188. lerp_fraction = fraction;
  189. #endif
  190. #if RBODYHISTORY_LERP_CORRECTION
  191. lerp_fraction = fraction * (RBODY_HISTORY_MIN_TIME - SnapshotArray[index].Age) / RBODY_HISTORY_MIN_TIME;
  192. #endif
  193. RigidBodyStateStruct::Lerp(SnapshotArray[index],ideal_state,lerp_fraction,&(SnapshotArray[index]));
  194. }
  195. #endif
  196. }
  197. void RBodyHistoryClass::Find_Nearest_State(const RigidBodyStateStruct & input,RigidBodyStateStruct * output)
  198. {
  199. /*
  200. ** Find the nearest line segment
  201. */
  202. float min_dist = 100000.0f;
  203. float result_fraction = 0.0f;
  204. int result_index0 = -1;
  205. int result_index1 = -1;
  206. for (int counter=0; counter<RBODY_SNAPSHOT_COUNT-1; counter++) {
  207. int index0 = Wrap_Index(HeadIndex + counter);
  208. int index1 = Wrap_Index(index0 + 1);
  209. LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);
  210. Vector3 point = segment.Find_Point_Closest_To(input.Position);
  211. float dist = (point - input.Position).Length();
  212. float fraction = 0.0f;
  213. if (segment.Get_Length() > 0.0f) {
  214. fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
  215. }
  216. #if 0
  217. /*
  218. ** Ignore points with velocity more than 90 deg away from server vel
  219. */
  220. float vdot = 0.0f;
  221. Vector3 history_vel;
  222. Vector3::Lerp(SnapshotArray[index0].Velocity,SnapshotArray[index1].Velocity,fraction,&history_vel);
  223. vdot = Vector3::Dot_Product(vel,history_vel);
  224. #endif
  225. if ((dist < min_dist) /*&& (vdot >= 0.0f)*/) {
  226. min_dist = dist;
  227. result_fraction = fraction;
  228. result_index0 = index0;
  229. result_index1 = index1;
  230. }
  231. }
  232. WWASSERT((result_index0 >= 0) && (result_index0 < RBODY_SNAPSHOT_COUNT));
  233. WWASSERT((result_index1 >= 0) && (result_index1 < RBODY_SNAPSHOT_COUNT));
  234. WWASSERT(output != NULL);
  235. RigidBodyStateStruct::Lerp(SnapshotArray[result_index0],SnapshotArray[result_index1],result_fraction,output);
  236. }
  237. void RBodyHistoryClass::StateSnapshotStruct::Lerp(const StateSnapshotStruct & a, const StateSnapshotStruct & b, float fraction)
  238. {
  239. RigidBodyStateStruct::Lerp(a,b,fraction,this);
  240. Age = WWMath::Lerp(a.Age,b.Age,fraction);
  241. }
  242. /***********************************************************************************************
  243. **
  244. ** RigidBodyClass Implementation
  245. **
  246. ***********************************************************************************************/
  247. float DEFAULT_CONTACT_THICKNESS = 0.2f;
  248. #define IMPULSE_COLOR Vector3(1,0,0)
  249. #define LMOMENTUM_COLOR Vector3(0,1,0)
  250. #define AMOMENTUM_COLOR Vector3(0,0,1)
  251. /*
  252. ** Declare a PersistFactory for RigidBodyClasses
  253. */
  254. SimplePersistFactoryClass<RigidBodyClass,PHYSICS_CHUNKID_RIGIDBODY> _RigidBodyFactory;
  255. /*
  256. ** Chunk ID's used by RigidBodyClass
  257. */
  258. enum
  259. {
  260. RBODY_CHUNK_MOVEABLE = 0x00800900,
  261. RBODY_CHUNK_VARIABLES,
  262. RBODY_VARIABLE_ODESYSTEM_PTR = 0x00,
  263. RBODY_VARIABLE_IBODY,
  264. RBODY_VARIABLE_IBODYINV,
  265. RBODY_VARIABLE_STATE_POSITION,
  266. RBODY_VARIABLE_STATE_ORIENTATION,
  267. RBODY_VARIABLE_STATE_LMOMENTUM,
  268. RBODY_VARIABLE_STATE_AMOMENTUM,
  269. RBODY_VARIABLE_CONTACT_STIFFNESS, // OBSOLETE!
  270. RBODY_VARIABLE_CONTACT_DAMPING, // OBSOLETE!
  271. RBODY_VARIABLE_CONTACT_LENGTH,
  272. };
  273. /*
  274. ** Rigid Body Dynamics
  275. **
  276. ** NOTES:
  277. ** - No matter what numerical integration technique I use, it seems that the
  278. ** orientation drifts. I have to re-normalize frequently so the higher order
  279. ** integrators are not very attractive (more frequent calls to Compute_Forces...).
  280. ** - Clamping angular velocity to an artificial maximum seems to cause problems
  281. ** as well. I'm not sure why but in the test app, it causes "jumpiness" in
  282. ** the simulation.
  283. ** - Impact/Contact plan: Integrate a new state, ignoring collision but computing
  284. ** penalty forces. Then break that change in state into a change in orientation
  285. ** along with a change in position. Try the change in orientation, checking for
  286. ** penetration; if penetration occurs, discard the orientation change. Try
  287. ** the translation, using the swept OBBox. Compute impact impluse if contact
  288. ** occurs. When testing for penetration/impact use the normal collision box.
  289. ** When computing penalty/contact forces use a slightly larger box and collect
  290. ** the points inside the box and the box points "behind" the triangles.
  291. **
  292. ** May 3, 1999
  293. ** - The idea about breaking the state update into separate orientation and translation
  294. ** updates is completely flawed. It just doesn't work. Trying to find a new way
  295. ** of detecting collisions without resorting to the binary search through time!
  296. ** - Binary searching for the time-of-collision requires a different sort of collision
  297. ** query. While iterating, it requires a simpler intersection query. Once finished
  298. ** iterating (found the TOC) it requires a query that will return what point is
  299. ** causing the collision.
  300. **
  301. ** June 25, 1999
  302. ** - Completely rigid motion may be less efficient in high-poly environments. For
  303. ** example, a rigid object sliding along the ground will experience a collision (and
  304. ** therefore rewind the integrator) at every single new non-coplanar triangle.
  305. ** - On the other hand, the buggy seems to go over curvy surfaces fine since its springs
  306. ** just adjust over time. Maybe penalty forces are the way after all... Going to have
  307. ** to try the two-layer box idea...
  308. ** - Updating the integrator to a scheme that can handle multiple systems being combined
  309. ** into a single coupled system.
  310. **
  311. ** August 16, 1999
  312. ** New Idea: Two-layer box with TOC searching and penalty based contact forces. General
  313. ** idea is that we let things interpenetrate their outer boxes but not their inner ones.
  314. **
  315. ** pseudo-code:
  316. ** - collect all rigid bodies into a list (later on, we'll break into multiple lists)
  317. ** - integrate them all, computing penalty or real contact forces at each active contact
  318. ** - if interpenetration occurs:
  319. ** - search for TOC
  320. ** - rewind (interpolate) all objects to TOC
  321. ** - find the colliding features on the objects involved (edge->edge or vertex->face)
  322. ** - apply collision impulses to the two colliding objects
  323. ** - add contact if resultant relative velocity is less than epsilon
  324. **
  325. ** Derivative calculations contact, constraint forces:
  326. ** - zero each object's force accumulator (or reset it with gravity)
  327. ** - apply all external forces (springs, wind, water)
  328. ** - iterate over all contacts in the sytem, either putting them into a big contact solver
  329. ** or computing a penalty based on their displacement.
  330. ** - add the contact forces to all of the relavent objects
  331. ** - have each object compute its derivatives and add them to the vector
  332. **
  333. ** Finding point of collision:
  334. ** - binary search time until the objects are within some distance of each other
  335. ** - perform an intersection check at time t+dt and record the polygons/boxes that penetrate
  336. ** the outer skin
  337. ** - perform distance calculations on all of those objects (polygons/boxes) at time t
  338. ** - determine if the closest point is caused by a pair of edges or a vertex hitting a face
  339. ** - at the collision point, compute and apply the necessary collision impulse
  340. ** - if the resultant relative velocity is low enough, add a contact constraint/spring to the system
  341. **
  342. ** Sept 23, 1999
  343. ** Another new idea: Padded boxes.
  344. ** - The collision box for an object has four pads on each face (for a total of 24 pads). Each
  345. ** of these pads can have one contact point.
  346. ** - During each timestep (and sub-timestep) each pad will push out from its face until it hits something
  347. ** or reaches the edge of the outer box. If it hits something, a contact will be set up which records
  348. ** the position, normal, velocity, and surface parameters.
  349. ** - Normally all contacts could behave like damped springs.
  350. ** - If the inner box is penetrated, we could search for a time when the outer is penetrated, the inner
  351. ** isn't, and apply impulses to all incoming contact points.
  352. ** - Probably need to do some hierarchical culling to the contact checking, try the entire face and
  353. ** if it hits something, then try the four sub-faces.
  354. **
  355. ** Oct 14, 1999
  356. ** YAHPI - Yet Another Hacky Physics Idea: Octant Boxes
  357. ** - Divide our collision box into 8 octants and detect one contact per octant.
  358. ** - Contact detection will be achieved by using the box-sweeping code on each octant.
  359. ** - Octants start out inside the box and sweep along a diagonal some distance
  360. ** - At their starting positions these "octant boxes" need to overlap so that when at their
  361. ** fully extended position there aren't gaps around the model.
  362. ** ALSO: a key idea is to *never* binary search for the "real" collision. We just detect
  363. ** the collision, revert state, and divide our momentum down in the hopes that the forces
  364. ** will handle everything if we come in slower next frame. This code will need to be improved...
  365. **
  366. ** Oct 19, 1999
  367. ** Octant boxes are flawed because the contact point for each octant jitters around on the
  368. ** object when you're on flat ground. Since the boxes overlap, the points can all jitter
  369. ** to one side of the CM and then to another side. This is not good :-)
  370. ** Solution: Add contact "hairs" to the corners of the box and "prefer" them whenever the
  371. ** contact "compression" is close to that of its octant box.
  372. ** Remaining/New Problems:
  373. ** - Need to compute parameters for these "oct-boxes" automatically which handle any combination
  374. ** of OBBox + Mass
  375. ** - Objects can now "tunnel" need to do an extra collision check to prevent this.
  376. ** - Need to test the simulation code at the "worst-case" simulation framerate to see if it still
  377. ** stabilizes...
  378. ** - Need to handle object-object collisions.
  379. **
  380. ** Oct 26,1999
  381. ** - Good success with the octbox + corner contacts idea. Use both the corner contact and
  382. ** the octant contact when both are present and the octant is closer than the corner.
  383. ** - Also still using the "divide down the momentum" approach when there is a collision. Now
  384. ** the code increases the divisor each frame that the object is stuck. Not 100% sure if I
  385. ** like this but it is so much faster than doing all of those binary searches through time...
  386. ** - Critically damped or overdamped contact springs make the system too stiff. I use underdamped
  387. ** springs (0.33*critical) to keep everything numerically stable.
  388. ** - Had to re-normalize the orientation on every sub-timestep. It would be nice if we
  389. ** didn't have to do this. If the guts of the integrator was rotating the orientation
  390. ** instead of adding to it we probably could avoid this.
  391. ** - A first pass at (fake) friction is implemented but commented out. I'm currently getting
  392. ** instability if I turn it on and it also never stops the object on slopes, etc.
  393. ** TODO: write a custom integrator which rotates the orientation rather than doing the normal
  394. ** state += derivatives*dt... Is this possible? It is for Euler integration but we need
  395. ** at least MidPoint to keep the simulation stable.
  396. ** TODO: when a collision is detected, move the object to the extremeties of its contacts? This
  397. ** should help smooth out collisions? Will have to recursively try to move any objects in contact
  398. ** with this one too... Probably will need this recursive "fake move" in order to handle obj-obj
  399. ** interaction anyway... Hmmm, this needs more thought.
  400. ** TODO: handle obj-obj interaction :-)
  401. **
  402. ** Oct 11, 2001
  403. ** - Latency handling network code. Each packet causes us to compute an error between the
  404. ** nearest point in their history; then in the timestep function we try to correct that error.
  405. */
  406. /***********************************************************************************************
  407. * RigidBodyClass::RigidBodyClass -- Constructor for RigidBodyClass *
  408. * *
  409. * INPUT: *
  410. * *
  411. * OUTPUT: *
  412. * *
  413. * WARNINGS: *
  414. * *
  415. * HISTORY: *
  416. * 10/15/99 gth : Created. *
  417. *=============================================================================================*/
  418. RigidBodyClass::RigidBodyClass(void) :
  419. Box(NULL),
  420. ContactBox(NULL),
  421. IBody(1),
  422. IBodyInv(1),
  423. Rotation(1),
  424. IInv(1),
  425. Velocity(0,0,0),
  426. AngularVelocity(0,0,0),
  427. ContactCount(0),
  428. StickCount(0),
  429. LastTimestep(0.0f),
  430. GoToSleepTimer(RBODY_SLEEP_DELAY),
  431. History(NULL)
  432. {
  433. State.Position.Set(0,0,0);
  434. State.Orientation.Make_Identity();
  435. State.LMomentum.Set(0,0,0);
  436. State.AMomentum.Set(0,0,0);
  437. LatencyError.Position.Set(0,0,0);
  438. LatencyError.Orientation.Make_Identity();
  439. LatencyError.LMomentum.Set(0,0,0);
  440. LatencyError.AMomentum.Set(0,0,0);
  441. LastKnownState.Position.Set(0,0,0);
  442. LastKnownState.Orientation.Make_Identity();
  443. LastKnownState.LMomentum.Set(0,0,0);
  444. LastKnownState.AMomentum.Set(0,0,0);
  445. ContactThickness = DEFAULT_CONTACT_THICKNESS;
  446. }
  447. void RigidBodyClass::Init(const RigidBodyDefClass & def)
  448. {
  449. MoveablePhysClass::Init(def);
  450. State.Position.Set(0,0,0);
  451. State.Orientation.Make_Identity();
  452. State.LMomentum.Set(0,0,0);
  453. State.AMomentum.Set(0,0,0);
  454. LatencyError.Position.Set(0,0,0);
  455. LatencyError.Orientation.Make_Identity();
  456. LatencyError.LMomentum.Set(0,0,0);
  457. LatencyError.AMomentum.Set(0,0,0);
  458. LastKnownState.Position.Set(0,0,0);
  459. LastKnownState.Orientation.Make_Identity();
  460. LastKnownState.LMomentum.Set(0,0,0);
  461. LastKnownState.AMomentum.Set(0,0,0);
  462. ContactThickness = DEFAULT_CONTACT_THICKNESS;
  463. GoToSleepTimer = RBODY_SLEEP_DELAY;
  464. }
  465. RigidBodyClass::~RigidBodyClass(void)
  466. {
  467. REF_PTR_RELEASE(Box);
  468. if (ContactBox != NULL) {
  469. delete ContactBox;
  470. ContactBox = NULL;
  471. }
  472. }
  473. const AABoxClass & RigidBodyClass::Get_Bounding_Box(void) const
  474. {
  475. // TODO: propogate this into Phys?
  476. WWASSERT(Model);
  477. return Model->Get_Bounding_Box();
  478. }
  479. const Matrix3D & RigidBodyClass::Get_Transform(void) const
  480. {
  481. // TODO: propogate this into Phys?
  482. WWASSERT(Model);
  483. return Model->Get_Transform();
  484. }
  485. void RigidBodyClass::Set_Transform(const Matrix3D & m)
  486. {
  487. WWASSERT(Model);
  488. //TODO: rename this to Teleport!
  489. //TODO: warp model to desired position and verify that it is non-intersecting
  490. State.Orientation = Build_Quaternion(m);
  491. State.Position = m.Get_Translation();
  492. Model->Set_Transform(m);
  493. Set_Flag(ASLEEP,false);
  494. // each time the state changes, update the derived values
  495. Update_Auxiliary_State();
  496. Update_Cull_Box();
  497. Update_Visibility_Status();
  498. }
  499. bool RigidBodyClass::Cast_Ray(PhysRayCollisionTestClass & raytest)
  500. {
  501. if (Model && Model->Cast_Ray(raytest)) {
  502. raytest.CollidedPhysObj = this;
  503. return true;
  504. }
  505. return false;
  506. }
  507. bool RigidBodyClass::Cast_AABox(PhysAABoxCollisionTestClass & boxtest)
  508. {
  509. // only let others collide against what we use to collide with them...
  510. if (CollisionMath::Collide(boxtest.Box,boxtest.Move,ContactBox->Get_World_Inner_Box(),Vector3(0,0,0),boxtest.Result)) {
  511. boxtest.CollidedPhysObj = this;
  512. boxtest.CollidedRenderObj = Box;
  513. return true;
  514. }
  515. return false;
  516. }
  517. bool RigidBodyClass::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest)
  518. {
  519. // only let others collide against what we use to collide with them...
  520. if (CollisionMath::Collide(boxtest.Box,boxtest.Move,ContactBox->Get_World_Inner_Box(),Vector3(0,0,0),boxtest.Result)) {
  521. boxtest.CollidedPhysObj = this;
  522. boxtest.CollidedRenderObj = Box;
  523. return true;
  524. }
  525. return false;
  526. }
  527. bool RigidBodyClass::Intersection_Test(PhysAABoxIntersectionTestClass & test)
  528. {
  529. if (CollisionMath::Intersection_Test(ContactBox->Get_World_Inner_Box(),test.Box)) {
  530. test.Add_Intersected_Object(this);
  531. return true;
  532. }
  533. return false;
  534. }
  535. bool RigidBodyClass::Intersection_Test(PhysOBBoxIntersectionTestClass & test)
  536. {
  537. if (CollisionMath::Intersection_Test(ContactBox->Get_World_Inner_Box(),test.Box)) {
  538. test.Add_Intersected_Object(this);
  539. return true;
  540. }
  541. return false;
  542. }
  543. bool RigidBodyClass::Intersection_Test(PhysMeshIntersectionTestClass & test)
  544. {
  545. // flip the test around and test our OBBox against the given mesh...
  546. OBBoxIntersectionTestClass our_test(ContactBox->Get_World_Inner_Box(),test.CollisionType);
  547. if (test.Mesh->Intersect_OBBox(our_test)) {
  548. test.Add_Intersected_Object(this);
  549. return true;
  550. }
  551. return false;
  552. }
  553. bool RigidBodyClass::Can_Teleport(const Matrix3D &new_tm, bool check_dyn_only,NonRefPhysListClass * result_list)
  554. {
  555. bool intersecting = false;
  556. if (ContactBox) {
  557. ContactBox->Set_Transform(new_tm);
  558. intersecting = ContactBox->Is_Intersecting(result_list,!check_dyn_only,true);
  559. ContactBox->Set_Transform(Get_Transform());
  560. }
  561. return !intersecting;
  562. }
  563. bool RigidBodyClass::Can_Teleport_And_Stand(const Matrix3D &new_tm, Matrix3D *out)
  564. {
  565. *out = new_tm;
  566. return Can_Teleport(new_tm);
  567. }
  568. void RigidBodyClass::Set_Model(RenderObjClass * model)
  569. {
  570. MoveablePhysClass::Set_Model(model);
  571. Update_Cached_Model_Parameters();
  572. }
  573. void RigidBodyClass::Update_Cached_Model_Parameters(void)
  574. {
  575. // if we don't have a model yet, just return
  576. if (Model == NULL) return;
  577. Set_Transform(Model->Get_Transform());
  578. // cache a pointer to the collision box...
  579. REF_PTR_RELEASE(Box);
  580. // Try to get the "WorldBox" from the model
  581. if (Model) {
  582. RenderObjClass * obj = Model->Get_Sub_Object_By_Name("WorldBox");
  583. if (obj && obj->Class_ID() == RenderObjClass::CLASSID_OBBOX) {
  584. REF_PTR_SET(Box,(OBBoxRenderObjClass *)obj);
  585. }
  586. REF_PTR_RELEASE(obj);
  587. // If we didn't finde WorldBox, try to find the LOD named "WorldBox"
  588. // The LOD code generates a unique name for the mesh by appending A,B,C, etc to the name.
  589. // A is the lowest LOD, B is the next, and so on. Our worldbox is specified in the highest
  590. // LOD so we have to construct the name by appending 'A'+LodCount to the name... icky
  591. if ((Box == NULL) && (Model->Class_ID() == RenderObjClass::CLASSID_HLOD)) {
  592. char namebuffer[64];
  593. sprintf(namebuffer,"WorldBox%c",'A' + ((HLodClass *)Model)->Get_Lod_Count() - 1);
  594. obj = Model->Get_Sub_Object_By_Name(namebuffer);
  595. if (obj && obj->Class_ID() == RenderObjClass::CLASSID_OBBOX) {
  596. REF_PTR_SET(Box,(OBBoxRenderObjClass *)obj);
  597. }
  598. REF_PTR_RELEASE(obj);
  599. }
  600. }
  601. // Otherwise just create one
  602. if (Box == NULL) {
  603. WWDEBUG_SAY(("Missing WorldBox in model: %s\r\n",Model->Get_Name()));
  604. Box = new OBBoxRenderObjClass(OBBoxClass(Vector3(0,0,0),Vector3(1,1,1)));
  605. Box->Set_Collision_Type(COLLISION_TYPE_PHYSICAL);
  606. }
  607. // Update our contact box
  608. Matrix3D tm = Get_Transform();
  609. Model->Set_Transform(Matrix3D(1));
  610. if (ContactBox != NULL) {
  611. delete ContactBox;
  612. }
  613. ContactBox = new OctBoxClass(*this,Box->Get_Box());
  614. ContactBox->Set_Thickness(ContactThickness);
  615. ContactBox->Update_Contact_Parameters();
  616. Model->Set_Transform(tm);
  617. ContactBox->Set_Transform(tm);
  618. // recompute our inertia tensor
  619. Compute_Inertia();
  620. // and update our auxiliary state (inertia effects it)
  621. Update_Auxiliary_State();
  622. // update our culling box
  623. Update_Cull_Box();
  624. }
  625. void RigidBodyClass::Get_Velocity(Vector3 * set_vel) const
  626. {
  627. *set_vel = Velocity;
  628. Assert_State_Valid();
  629. }
  630. void RigidBodyClass::Get_Angular_Velocity(Vector3 * set_avel) const
  631. {
  632. *set_avel = AngularVelocity;
  633. Assert_State_Valid();
  634. }
  635. void RigidBodyClass::Set_Velocity(const Vector3 & newvel)
  636. {
  637. Assert_State_Valid();
  638. #ifdef WWDEBUG
  639. if (newvel.Is_Valid() != true) {
  640. WWDEBUG_SAY(("someone set an invalid velocity (%8.3f, %8.3f, %8.3f)\r\n",newvel.X,newvel.Y,newvel.Z));
  641. }
  642. #endif
  643. Velocity = newvel;
  644. State.LMomentum = Velocity * Mass;
  645. Assert_State_Valid();
  646. }
  647. void RigidBodyClass::Set_Angular_Velocity(const Vector3 & newavel)
  648. {
  649. WWASSERT(WWMath::Is_Valid_Float(newavel.X));
  650. WWASSERT(WWMath::Is_Valid_Float(newavel.Y));
  651. WWASSERT(WWMath::Is_Valid_Float(newavel.Z));
  652. #ifdef WWDEBUG
  653. if (newavel.Is_Valid() != true) {
  654. WWDEBUG_SAY(("someone set an invalid angular velocity (%8.3f, %8.3f, %8.3f)\r\n",newavel.X,newavel.Y,newavel.Z));
  655. }
  656. #endif
  657. AngularVelocity = newavel;
  658. Matrix3 I = IInv.Inverse();
  659. State.AMomentum = I * newavel;
  660. Assert_State_Valid();
  661. }
  662. void RigidBodyClass::Network_Interpolate_State_Update
  663. (
  664. const Vector3 & new_pos,
  665. const Quaternion & new_orientation,
  666. const Vector3 & new_vel,
  667. const Vector3 & new_avel,
  668. float fraction
  669. )
  670. {
  671. // If we are severely out of sync with the network update, we have to "pop"
  672. // to the new state. If needed, we can add more criteria here in the future...
  673. float characteristic_length2 = 4.0f * ContactBox->Get_Extent_Length2();
  674. if ((new_pos - State.Position).Length2() > characteristic_length2) {
  675. fraction = 1.0f;
  676. }
  677. // interpolate a new state
  678. Vector3::Lerp(State.Position,new_pos,fraction,&(State.Position));
  679. ::Fast_Slerp(State.Orientation,State.Orientation,new_orientation,fraction);
  680. Vector3 vel,avel;
  681. Vector3::Lerp(Velocity,new_vel,fraction,&vel);
  682. Vector3::Lerp(AngularVelocity,new_avel,fraction,&avel);
  683. Set_Velocity(vel);
  684. Set_Angular_Velocity(avel);
  685. Assert_State_Valid();
  686. // clear the latency error since we are not using it
  687. LatencyError.Reset();
  688. // each time the state changes, update the derived values
  689. Update_Auxiliary_State();
  690. Model->Set_Transform(Matrix3D(Rotation,State.Position));
  691. Update_Cull_Box();
  692. Update_Visibility_Status();
  693. }
  694. void RigidBodyClass::Network_Latency_State_Update
  695. (
  696. const Vector3 & new_pos,
  697. const Quaternion & new_orientation,
  698. const Vector3 & new_vel,
  699. const Vector3 & new_avel
  700. )
  701. {
  702. OBBoxClass debug_box;
  703. ContactBox->Get_Inner_Box(&debug_box,new_orientation,new_pos);
  704. DEBUG_RENDER_OBBOX(debug_box,Vector3(1.0f,0,0),0.3f);
  705. /*
  706. ** Set up the input state
  707. */
  708. LastKnownState.Position = new_pos;
  709. LastKnownState.Orientation = new_orientation;
  710. LastKnownState.LMomentum = new_vel * Mass;
  711. Matrix3 I = IInv.Inverse();
  712. LastKnownState.AMomentum = I * new_avel;
  713. /*
  714. ** Allocate a history object if needed
  715. */
  716. if (History == NULL) {
  717. History = new RBodyHistoryClass;
  718. History->Init(LastKnownState);
  719. }
  720. WWASSERT(History != NULL);
  721. /*
  722. ** Search our history to find the point nearest this server update
  723. */
  724. RigidBodyStateStruct nearest_state;
  725. History->Find_Nearest_State(LastKnownState,&nearest_state);
  726. /*
  727. ** Now, compute the correction to apply to our current state
  728. */
  729. LatencyError.Position = LastKnownState.Position - nearest_state.Position;
  730. LatencyError.Orientation = LastKnownState.Orientation / nearest_state.Orientation;
  731. LatencyError.LMomentum = LastKnownState.LMomentum - nearest_state.LMomentum;
  732. LatencyError.AMomentum= LastKnownState.AMomentum - nearest_state.AMomentum;
  733. }
  734. void RigidBodyClass::Network_Latency_Error_Correction(float dt)
  735. {
  736. if (LatencyError.Position.Length2() > 0.01f) {
  737. #ifdef WWDEBUG
  738. if (!LatencyError.Is_Valid()) {
  739. WWDEBUG_SAY(("RigidBodyClass - Invalid Latency Error!\r\n"));
  740. WWDEBUG_SAY((" position error: %f, %f, %f\r\n",LatencyError.Position.X,LatencyError.Position.Y,LatencyError.Position.Z));
  741. WWDEBUG_SAY((" orientation error: %f, %f, %f, %f\r\n",LatencyError.Orientation.X, LatencyError.Orientation.Y,LatencyError.Orientation.Z,LatencyError.Orientation.W));
  742. WWDEBUG_SAY((" L momentum error: %f, %f, %f\r\n",LatencyError.LMomentum.X,LatencyError.LMomentum.Y,LatencyError.LMomentum.Z));
  743. WWDEBUG_SAY((" A momentum error: %f, %f, %f\r\n",LatencyError.AMomentum.X,LatencyError.AMomentum.Y,LatencyError.AMomentum.Z));
  744. }
  745. #endif
  746. if (!LatencyError.Is_Valid()) {
  747. LatencyError.Reset();
  748. }
  749. /*
  750. ** Compute the theoretically fully corrected state
  751. */
  752. RigidBodyStateStruct ideal_state(State);
  753. ideal_state.Position += LatencyError.Position;
  754. ideal_state.Orientation = ideal_state.Orientation * LatencyError.Orientation;
  755. ideal_state.LMomentum += LatencyError.LMomentum;
  756. ideal_state.AMomentum += LatencyError.AMomentum;
  757. #if 0
  758. WWDEBUG_SAY(("Rigid Body %s network correction\r\n",Model->Get_Name()));
  759. WWDEBUG_SAY((" position error: %f\r\n",LatencyError.Position.Length()));
  760. WWDEBUG_SAY((" orientation error: %f\r\n",WWMath::Acos(LatencyError.Orientation.W) * 2.0f));
  761. #endif
  762. /*
  763. ** Decide whether to do the normal smooth correction or to pop
  764. ** We'll pop whenever the position error is large and the
  765. ** velocity sent from the server is small.
  766. */
  767. const float POP_POSITION_ERROR2 = 3.0f * 3.0f;
  768. const float POP_MAX_VELOCITY2 = 0.5f * 0.5f;
  769. const float POP_ANGLE_ERROR = 0.966f; // cos(theta/2) for 30 degrees
  770. bool pop = ( (LatencyError.Position.Length2() > POP_POSITION_ERROR2) &&
  771. ((LastKnownState.LMomentum * MassInv).Length2() < POP_MAX_VELOCITY2));
  772. float cos_half_theta = LatencyError.Orientation.W;
  773. if (WWMath::Fabs(cos_half_theta) < POP_ANGLE_ERROR) {
  774. pop = true;
  775. }
  776. if (pop == true) {
  777. /*
  778. ** Jump to the last given state
  779. */
  780. State = LastKnownState;
  781. History->Init(LastKnownState);
  782. /*
  783. ** Clear the latency error
  784. */
  785. LatencyError.Position.Set(0,0,0);
  786. LatencyError.Orientation.Set(0,0,0);
  787. LatencyError.LMomentum.Set(0,0,0);
  788. LatencyError.AMomentum.Set(0,0,0);
  789. } else {
  790. /*
  791. ** Blend a fraction of this "ideal" state into the current state
  792. */
  793. float fraction = 0.1f;
  794. if (_CorrectionTime <= dt) {
  795. fraction = 1.0f;
  796. } else {
  797. fraction = WWMath::Clamp(dt / _CorrectionTime);
  798. }
  799. fraction = WWMath::Clamp(fraction,0.0f,0.5f);
  800. RigidBodyStateStruct::Lerp(State,ideal_state,fraction,&State);
  801. History->Apply_Correction(LatencyError,fraction);
  802. /*
  803. ** Recalculate the remaining error
  804. */
  805. LatencyError.Position = ideal_state.Position - State.Position;
  806. LatencyError.Orientation = ideal_state.Orientation / State.Orientation;
  807. LatencyError.LMomentum = ideal_state.LMomentum - State.LMomentum;
  808. LatencyError.AMomentum = ideal_state.AMomentum - State.AMomentum;
  809. }
  810. /*
  811. ** each time the state changes, update the derived values
  812. */
  813. Assert_State_Valid();
  814. Update_Auxiliary_State();
  815. Model->Set_Transform(Matrix3D(Rotation,State.Position));
  816. Update_Cull_Box();
  817. Update_Visibility_Status();
  818. }
  819. }
  820. void RigidBodyClass::Apply_Impulse(const Vector3 & imp)
  821. {
  822. // Impluse applied to center of mass simply adds to the linear momentum
  823. State.LMomentum += imp;
  824. Velocity = State.LMomentum * MassInv;
  825. if (Is_Asleep()) {
  826. Set_Flag(ASLEEP,false);
  827. }
  828. Assert_State_Valid();
  829. DEBUG_RENDER_VECTOR(State.Position,imp,IMPULSE_COLOR);
  830. }
  831. void RigidBodyClass::Apply_Impulse(const Vector3 & imp, const Vector3 & wpos)
  832. {
  833. // Impluse applied off center, adds to the linear momentum and also
  834. // adds to the torque.
  835. Vector3 aimp;
  836. Vector3::Cross_Product((wpos - State.Position),imp,&aimp);
  837. State.LMomentum += imp;
  838. State.AMomentum += aimp;
  839. Velocity = MassInv * State.LMomentum;
  840. AngularVelocity = IInv * State.AMomentum;
  841. if (Is_Asleep()) {
  842. Set_Flag(ASLEEP,false);
  843. }
  844. Assert_State_Valid();
  845. #if RBODY_DEBUGGING
  846. if (RBODY_DEBUG_FILTER) {
  847. WWDEBUG_SAY(("Impulse applied: %10.10f %10.10f %10.10f\r\n",imp.X,imp.Y,imp.Z));
  848. DEBUG_RENDER_VECTOR(wpos,imp,IMPULSE_COLOR);
  849. }
  850. #endif
  851. }
  852. void RigidBodyClass::Set_Mass(float mass)
  853. {
  854. // In this function, we need to update the inertia tensor and
  855. // adjust the linear and angular momentum so that the object
  856. // retains the same velocity and angular velocity.
  857. Vector3 vel,avel;
  858. Get_Velocity(&vel);
  859. Get_Angular_Velocity(&avel);
  860. MoveablePhysClass::Set_Mass(mass);
  861. Compute_Inertia();
  862. Update_Auxiliary_State();
  863. Set_Velocity(vel);
  864. Set_Angular_Velocity(avel);
  865. ContactBox->Update_Contact_Parameters();
  866. Assert_State_Valid();
  867. }
  868. void RigidBodyClass::Get_Inertia_Inv(Matrix3 * set_I_inv)
  869. {
  870. *set_I_inv = IInv;
  871. }
  872. void RigidBodyClass::Set_Inertia(const Matrix3 & I)
  873. {
  874. Vector3 vel,avel;
  875. Get_Velocity(&vel);
  876. Get_Angular_Velocity(&avel);
  877. IBody = I;
  878. IBodyInv = IBody.Inverse();
  879. Update_Auxiliary_State();
  880. Set_Velocity(vel);
  881. Set_Angular_Velocity(avel);
  882. }
  883. void RigidBodyClass::Get_Inertia(Matrix3 * I)
  884. {
  885. WWASSERT(I);
  886. *I = IBody;
  887. }
  888. void RigidBodyClass::Set_Contact_Parameters(float length)
  889. {
  890. ContactThickness = length;
  891. ContactBox->Set_Thickness(ContactThickness);
  892. }
  893. void RigidBodyClass::Get_Contact_Parameters(float * stiffness,float * damping,float * length)
  894. {
  895. if (stiffness) {
  896. if (ContactBox) {
  897. *stiffness = ContactBox->Get_Stiffness();
  898. } else {
  899. *stiffness = 0.0f;
  900. }
  901. }
  902. if (damping) {
  903. if (ContactBox) {
  904. *damping = ContactBox->Get_Damping();
  905. } else {
  906. *damping = 0.0f;
  907. }
  908. }
  909. if (length) {
  910. *length = ContactThickness;
  911. }
  912. }
  913. int RigidBodyClass::Compute_Derivatives
  914. (
  915. float t,
  916. StateVectorClass * test_state,
  917. StateVectorClass * dydt,
  918. int index
  919. )
  920. {
  921. if (test_state) {
  922. Set_State(*test_state,index);
  923. }
  924. #if RBODY_DEBUGGING
  925. if (RBODY_DEBUG_FILTER) {
  926. WWDEBUG_SAY((" compute_derivatives: t = %f\r\n",t));
  927. Dump_State();
  928. }
  929. #endif
  930. // time derivitive of position
  931. (*dydt)[index++] = Velocity[0];
  932. (*dydt)[index++] = Velocity[1];
  933. (*dydt)[index++] = Velocity[2];
  934. // time derivitive of orientation
  935. Quaternion avel(AngularVelocity.X,AngularVelocity.Y,AngularVelocity.Z,0.0f);
  936. Quaternion qdot = 0.5 * avel * State.Orientation;
  937. (*dydt)[index++] = qdot[0];
  938. (*dydt)[index++] = qdot[1];
  939. (*dydt)[index++] = qdot[2];
  940. (*dydt)[index++] = qdot[3];
  941. // time derivitives of momentum and angular momentum (a.k.a. force and torque)
  942. Vector3 force(0,0,0);
  943. Vector3 torque(0,0,0);
  944. Compute_Force_And_Torque(&force,&torque);
  945. WWASSERT(force.Is_Valid());
  946. WWASSERT(torque.Is_Valid());
  947. #if RBODY_DEBUGGING
  948. if (RBODY_DEBUG_FILTER) {
  949. WWDEBUG_SAY(("Force: %10.10f , %10.10f , %10.10f\r\n",force[0],force[1],force[2]));
  950. WWDEBUG_SAY(("Torque: %10.10f , %10.10f , %10.10f\r\n",torque[0],torque[1],torque[2]));
  951. }
  952. #endif
  953. (*dydt)[index++] = force[0];
  954. (*dydt)[index++] = force[1];
  955. (*dydt)[index++] = force[2];
  956. (*dydt)[index++] = torque[0];
  957. (*dydt)[index++] = torque[1];
  958. (*dydt)[index++] = torque[2];
  959. #if RBODY_DEBUGGING
  960. if (RBODY_DEBUG_FILTER) {
  961. WWDEBUG_SAY((" done. \r\n\r\n"));
  962. }
  963. #endif
  964. return index;
  965. }
  966. void RigidBodyClass::Get_State(StateVectorClass & set_state)
  967. {
  968. State.To_Vector(set_state);
  969. }
  970. int RigidBodyClass::Set_State(const StateVectorClass & new_state,int index)
  971. {
  972. WWPROFILE("RBody::Set_State");
  973. index = State.From_Vector(new_state,index);
  974. Update_Auxiliary_State();
  975. return index;
  976. }
  977. void RigidBodyClass::Set_State(const RigidBodyStateStruct & new_state)
  978. {
  979. State = new_state;
  980. Update_Auxiliary_State();
  981. }
  982. void RigidBodyClass::Integrate(float time)
  983. {
  984. Assert_State_Valid();
  985. //IntegrationSystem::Euler_Integrate(this,time);
  986. IntegrationSystem::Midpoint_Integrate(this,time);
  987. //IntegrationSystem::Runge_Kutta_Integrate(this,time);
  988. // normalize the orientation since it slowly drifts due to integrator error
  989. State.Orientation.Normalize();
  990. Update_Auxiliary_State();
  991. Assert_State_Valid();
  992. }
  993. void RigidBodyClass::Compute_Inertia(void)
  994. {
  995. // I'm assuming that the CM is at the origin and the principal axes of inertia
  996. // are aligned with the coordinate system. So I'm approximating I by using the
  997. // formula for a box which extends both direction in the maximum that the actual
  998. // box entends in either...
  999. IBody.Make_Identity();
  1000. if (Box) {
  1001. AABoxClass objbox;
  1002. Box->Get_Obj_Space_Bounding_Box(objbox);
  1003. float dx = 2.0f * objbox.Extent.X + WWMath::Fabs(objbox.Center.X);
  1004. float dy = 2.0f * objbox.Extent.Y + WWMath::Fabs(objbox.Center.Y);
  1005. float dz = 2.0f * objbox.Extent.Z + WWMath::Fabs(objbox.Center.Z);
  1006. IBody[0][0] = (1.0f / 12.0f) * Mass * (dz*dz + dy*dy);
  1007. IBody[1][1] = (1.0f / 12.0f) * Mass * (dx*dx + dz*dz);
  1008. IBody[2][2] = (1.0f / 12.0f) * Mass * (dx*dx + dy*dy);
  1009. }
  1010. IBodyInv = IBody.Inverse();
  1011. }
  1012. void RigidBodyClass::Update_Auxiliary_State(void)
  1013. {
  1014. WWPROFILE("Rbody::Assert_State_Valid");
  1015. Assert_State_Valid();
  1016. State.Orientation.Normalize();
  1017. // compute Rotation,IInv,Velocity,AngularVelocity
  1018. Rotation = Build_Matrix3(State.Orientation);
  1019. IInv = Rotation * IBodyInv * Rotation.Transpose();
  1020. Velocity = MassInv * State.LMomentum;
  1021. AngularVelocity = IInv * State.AMomentum;
  1022. if (ContactBox) {
  1023. ContactBox->Set_Transform(State.Orientation,State.Position);
  1024. }
  1025. Assert_State_Valid();
  1026. }
  1027. void RigidBodyClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
  1028. {
  1029. WWPROFILE("RigidBodyClass::Compute_Force_And_Torque");
  1030. // NOTE: derived classes should perform their force calculations first and
  1031. // then call us so because contact forces are computed at the end of
  1032. // this routine.
  1033. // NOTE: need to optimize this routine!
  1034. // add in gravity
  1035. *force += GravScale * Mass * PhysicsConstants::GravityAcceleration;
  1036. // Add damping
  1037. Vector3 vel_dir = Velocity;
  1038. if (vel_dir.Length2() > WWMATH_EPSILON) {
  1039. vel_dir.Normalize();
  1040. // DEBUG DEBUG
  1041. #pragma message ("(gth) HACK! zeroing bad velocities.")
  1042. const float MAX_VEL = 500.0f;
  1043. const float MAX_ACCEL = 100.0f;
  1044. if ( (Velocity.Is_Valid() == false) ||
  1045. (force->Is_Valid() == false) ||
  1046. (Velocity.Length2() > MAX_VEL * MAX_VEL) ||
  1047. (force->Length() * MassInv > MAX_ACCEL) )
  1048. {
  1049. WWDEBUG_SAY(("clearing velocity, model=%s vel=(%f,%f,%f)\r\n",Model->Get_Name(),Velocity.X,Velocity.Y,Velocity.Z));
  1050. Velocity.Set(0,0,0);
  1051. AngularVelocity.Set(0,0,0);
  1052. State.LMomentum.Set(0,0,0);
  1053. State.AMomentum.Set(0,0,0);
  1054. force->Set(0,0,0);
  1055. torque->Set(0,0,0);
  1056. vel_dir.Set(0,0,0);
  1057. }
  1058. RigidBodyDefClass * def = Get_RigidBodyDef();
  1059. *force -= def->AerodynamicDragCoefficient * Vector3::Dot_Product(Velocity,Velocity) * vel_dir;
  1060. WWASSERT(force->Is_Valid());
  1061. }
  1062. Vector3 a_dir = AngularVelocity;
  1063. // DEBUG DEBUG
  1064. #pragma message ("(gth) HACK! zeroing bad angular velocities.")
  1065. const float MAX_AVEL = 5.0f * 2.0f * WWMATH_PI;
  1066. if (a_dir.Length2() > MAX_AVEL * MAX_AVEL) {
  1067. WWDEBUG_SAY(("clearing angluar velocity, model=%s avel=(%f,%f,%f)\r\n",Model->Get_Name(),AngularVelocity.X,AngularVelocity.Y,AngularVelocity.Z));
  1068. Velocity.Set(0,0,0);
  1069. AngularVelocity.Set(0,0,0);
  1070. State.LMomentum.Set(0,0,0);
  1071. State.AMomentum.Set(0,0,0);
  1072. force->Set(0,0,0);
  1073. torque->Set(0,0,0);
  1074. a_dir.Set(0,0,0);
  1075. }
  1076. if (a_dir.Length2() > WWMATH_EPSILON) {
  1077. a_dir.Normalize();
  1078. WWASSERT((a_dir.Length() - 1.0f) < WWMATH_EPSILON);
  1079. *torque -= PhysicsConstants::AngularDamping * Vector3::Dot_Product(AngularVelocity,AngularVelocity) * a_dir;
  1080. WWASSERT(torque->Is_Valid());
  1081. }
  1082. // TODO: boyancy forces, force fields
  1083. // Detect contacts.
  1084. if (Get_RigidBodyDef()->CollisionDisabled == false) {
  1085. ContactBox->Compute_Contacts(false);
  1086. for (int i=0; i<ContactBox->Get_Contact_Count(); i++) {
  1087. const CastResultStruct & contact = ContactBox->Get_Contact(i);
  1088. Vector3 r;
  1089. Vector3::Subtract(contact.ContactPoint,State.Position,&r);
  1090. /*
  1091. ** Compute Contact Force
  1092. */
  1093. Vector3 pdot;
  1094. Compute_Point_Velocity(contact.ContactPoint,&pdot);
  1095. float dx = 1.0f - contact.Fraction;
  1096. float dv = Vector3::Dot_Product(pdot,contact.Normal);
  1097. float force_magnitude = ContactBox->Get_Stiffness()*dx - ContactBox->Get_Damping()*dv;
  1098. WWASSERT(WWMath::Is_Valid_Float(force_magnitude));
  1099. Vector3 contact_force = force_magnitude * contact.Normal;
  1100. Vector3 contact_torque;
  1101. Vector3::Cross_Product(r,contact_force,&contact_torque);
  1102. #ifdef WWDEBUG
  1103. if ((contact_force.Is_Valid() == false) || (contact_torque.Is_Valid() == false)) {
  1104. WWDEBUG_SAY(("bad contact: normal=(%8.3f,%8.3f,%8.3f) r=(%8.3f,%8.3f,%8.3f) dx=%8.3f dv=%8.3f\n",
  1105. contact.Normal.X,contact.Normal.Y,contact.Normal.Z,r.X,r.Y,r.Z,dx,dv));
  1106. contact_force.Set(0,0,0);
  1107. contact_torque.Set(0,0,0);
  1108. }
  1109. #endif
  1110. /*
  1111. ** If we are contacting a phys3 object, push it away from us. If we
  1112. ** can't push it away, then exert a contact force.
  1113. */
  1114. Phys3Class * p3obj = ContactBox->Peek_Contacted_Object(i)->As_Phys3Class();
  1115. bool resolved = false;
  1116. if (p3obj != NULL) {
  1117. resolved = Push_Phys3_Object_Away(p3obj,contact);
  1118. }
  1119. if (resolved == false) {
  1120. *force += contact_force;
  1121. *torque += contact_torque;
  1122. }
  1123. /*
  1124. ** Friction/Drag to bring the object to rest when contacting the ground
  1125. */
  1126. int contact_count = ContactBox->Get_Contact_Count();
  1127. float contact_weight = Get_Weight() / contact_count;
  1128. Vector3 gravity(0.0f,0.0f,-contact_weight);
  1129. if (Get_Flag(FRICTION_DISABLED) == false) {
  1130. Vector3 tan_vel = pdot - Vector3::Dot_Product(pdot,contact.Normal)*contact.Normal;
  1131. float tan_vel_magnitude = tan_vel.Length2();
  1132. float friction_coefficient = PhysicsConstants::Get_Contact_Friction_Coefficient(
  1133. PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
  1134. contact.SurfaceType );
  1135. if (tan_vel_magnitude > WWMATH_EPSILON * WWMATH_EPSILON) {
  1136. /*
  1137. ** Friction force points opposite the point's tangential motion
  1138. */
  1139. tan_vel_magnitude = WWMath::Sqrt(tan_vel_magnitude);
  1140. Vector3 tan_vel_dir = tan_vel / tan_vel_magnitude;
  1141. /*
  1142. ** The magnitude is the friction coefficient times the portion of the weight supported
  1143. ** by this contact. As the velocity approaches zero, this force approaches zero
  1144. */
  1145. float friction_magnitude = friction_coefficient * WWMath::Min(0.1f * tan_vel_magnitude * contact_weight,contact_weight);
  1146. Vector3 friction_force = -friction_magnitude * tan_vel_dir;
  1147. /*
  1148. ** The active contacts divy up the gravitational force to oppose
  1149. */
  1150. if (contact.Normal.Z > 0.0f) {
  1151. friction_force -= gravity - Vector3::Dot_Product(gravity,contact.Normal) * contact.Normal;
  1152. }
  1153. /*
  1154. ** Finally, clamp the force to the friction circle
  1155. */
  1156. float max_friction_force = friction_coefficient * contact_weight;
  1157. if (friction_force.Length2() > max_friction_force * max_friction_force) {
  1158. float flen = friction_force.Length();
  1159. friction_force /= flen;
  1160. friction_force *= 0.5f * max_friction_force;
  1161. }
  1162. /*
  1163. ** Compute the torque and add the force and torque into the accumulators
  1164. */
  1165. Vector3 friction_torque;
  1166. Vector3::Cross_Product(r,friction_force,&friction_torque);
  1167. *force += friction_force;
  1168. *torque += friction_torque;
  1169. DEBUG_RENDER_VECTOR(contact.ContactPoint,friction_force,Vector3(1,0,0));
  1170. }
  1171. }
  1172. DEBUG_RENDER_VECTOR(contact.ContactPoint,contact_force / Get_Mass(),Vector3(0,1,0));
  1173. }
  1174. }
  1175. /*
  1176. ** HACK! Never let the force or torque completely reflect the angular or linear momentum
  1177. ** clamp them to a value that at most, drives the momentum to zero
  1178. **
  1179. ** NOTE: this doesn't work, I'm just leaving it here in case I get a new idea
  1180. ** which will make it work...
  1181. */
  1182. #if JITTER_ELIMINATION_CODE
  1183. Vector3 max_delta_lmomentum = - 1.0f * State.LMomentum / LastTimestep;
  1184. Vector3 max_delta_amomentum = - 1.0f * State.AMomentum / LastTimestep;
  1185. Vector3 old_force = *force;
  1186. Vector3 old_torque = *torque;
  1187. if ((max_delta_lmomentum.X < 0) && (force->X < max_delta_lmomentum.X)) force->X = max_delta_lmomentum.X;
  1188. if ((max_delta_lmomentum.X > 0) && (force->X > max_delta_lmomentum.X)) force->X = max_delta_lmomentum.X;
  1189. if ((max_delta_lmomentum.Y < 0) && (force->Y < max_delta_lmomentum.Y)) force->Y = max_delta_lmomentum.Y;
  1190. if ((max_delta_lmomentum.Y > 0) && (force->Y > max_delta_lmomentum.Y)) force->Y = max_delta_lmomentum.Y;
  1191. if ((max_delta_lmomentum.Z < 0) && (force->Z < max_delta_lmomentum.Z)) force->Z = max_delta_lmomentum.Z;
  1192. if ((max_delta_lmomentum.Z > 0) && (force->Z > max_delta_lmomentum.Z)) force->Z = max_delta_lmomentum.Z;
  1193. if ((max_delta_amomentum.X < 0) && (torque->X < max_delta_amomentum.X)) torque->X = max_delta_amomentum.X;
  1194. if ((max_delta_amomentum.X > 0) && (torque->X > max_delta_amomentum.X)) torque->X = max_delta_amomentum.X;
  1195. if ((max_delta_amomentum.Y < 0) && (torque->Y < max_delta_amomentum.Y)) torque->Y = max_delta_amomentum.Y;
  1196. if ((max_delta_amomentum.Y > 0) && (torque->Y > max_delta_amomentum.Y)) torque->Y = max_delta_amomentum.Y;
  1197. if ((max_delta_amomentum.Z < 0) && (torque->Z < max_delta_amomentum.Z)) torque->Z = max_delta_amomentum.Z;
  1198. if ((max_delta_amomentum.Z > 0) && (torque->Z > max_delta_amomentum.Z)) torque->Z = max_delta_amomentum.Z;
  1199. WWASSERT(1.00001f * old_force.Length2() >= force->Length2());
  1200. WWASSERT(1.00001f * old_torque.Length2() >= torque->Length2());
  1201. #endif
  1202. WWASSERT(WWMath::Is_Valid_Float(force->X));
  1203. WWASSERT(WWMath::Is_Valid_Float(force->Y));
  1204. WWASSERT(WWMath::Is_Valid_Float(force->Z));
  1205. }
  1206. void RigidBodyClass::Compute_Point_Velocity(const Vector3 & p,Vector3 * pdot)
  1207. {
  1208. // REMEMBER: p is assumed to be in world coordinates! pdot is as well.
  1209. // pdot = velocity + CROSS(angular_velocity , r)
  1210. Vector3 r;
  1211. Vector3::Subtract(p,State.Position,&r);
  1212. Vector3::Cross_Product(AngularVelocity,r,pdot);
  1213. Vector3::Add(*pdot,Velocity,pdot);
  1214. }
  1215. bool RigidBodyClass::Is_Colliding(const Vector3 & point, const Vector3 & normal)
  1216. {
  1217. Vector3 padot;
  1218. Compute_Point_Velocity(point,&padot);
  1219. float vrel = Vector3::Dot_Product(normal,padot);
  1220. return vrel < 0.0f;
  1221. }
  1222. void RigidBodyClass::Set_Moving_Collision_Region(float dt)
  1223. {
  1224. AABoxClass region;
  1225. ContactBox->Get_Outer_Bounds(&region); // start with bounds of collision box
  1226. // Recenter and enlarge to contain our velocity (scaled by a bit)
  1227. Vector3 move = 1.5f * Velocity * dt;
  1228. region.Center += 0.5f * move;
  1229. region.Extent.X += WWMath::Fabs(move.X);
  1230. region.Extent.Y += WWMath::Fabs(move.Y);
  1231. region.Extent.Z += WWMath::Fabs(move.Z);
  1232. // Now, scale to account for any rotational effects
  1233. region.Extent *= 2.0f;
  1234. PhysicsSceneClass::Get_Instance()->Set_Collision_Region(region,Get_Collision_Group());
  1235. }
  1236. void RigidBodyClass::Set_Stationary_Collision_Region(void)
  1237. {
  1238. AABoxClass region;
  1239. ContactBox->Get_Outer_Bounds(&region); // start with bounds of collision box
  1240. PhysicsSceneClass::Get_Instance()->Set_Collision_Region(region,Get_Collision_Group());
  1241. }
  1242. bool RigidBodyClass::Can_Go_To_Sleep(float dt)
  1243. {
  1244. /*
  1245. ** RigidBodies go to sleep if their oct-box is resting on at least three contacts and
  1246. ** their velocities are below some thresh-hold and their controller isn't doing anything.
  1247. */
  1248. if ((Controller != NULL) && (Controller->Is_Inactive() != true)) {
  1249. GoToSleepTimer = RBODY_SLEEP_DELAY;
  1250. return false;
  1251. }
  1252. const float VEL_THRESHHOLD = 0.05f;
  1253. const float AVEL_THRESHHOLD = 0.05f;
  1254. float max_lmomentum2 = Mass * VEL_THRESHHOLD * VEL_THRESHHOLD;
  1255. float max_amomentum2 = IBody[1][1] * AVEL_THRESHHOLD * AVEL_THRESHHOLD;
  1256. bool tried_to_sleep = false;
  1257. if ((ContactBox->ContactCount >= 3) || (Get_RigidBodyDef()->CollisionDisabled)) {
  1258. if ((State.LMomentum.Length2() < max_lmomentum2) &&
  1259. (State.AMomentum.Length2() < max_amomentum2) )
  1260. {
  1261. tried_to_sleep = true;
  1262. if (GoToSleepTimer < 0.0f) {
  1263. return true;
  1264. }
  1265. }
  1266. }
  1267. if (tried_to_sleep) {
  1268. GoToSleepTimer -= dt;
  1269. } else {
  1270. GoToSleepTimer = RBODY_SLEEP_DELAY;
  1271. }
  1272. return false;
  1273. }
  1274. void RigidBodyClass::Compute_Impact(const CastResultStruct & result,Vector3 * impulse)
  1275. {
  1276. Compute_Impact(result.ContactPoint,result.Normal,impulse);
  1277. }
  1278. void RigidBodyClass::Compute_Impact(const Vector3 & point,const Vector3 & normal,Vector3 * impulse)
  1279. {
  1280. // NOTE: this is only a temporary solution, it assumes we are colliding with
  1281. // an immovable object (infinite mass)...
  1282. Vector3 padot;
  1283. Compute_Point_Velocity(point,&padot);
  1284. float vrel = Vector3::Dot_Product(normal,padot);
  1285. if (vrel > 0.0f) {
  1286. // moving away
  1287. impulse->Set(0,0,0);
  1288. } else {
  1289. // collision
  1290. float num = -(1.0f + Elasticity) * vrel;
  1291. Vector3 ra = point - State.Position;
  1292. Vector3 tmp1,tmp2;
  1293. Vector3::Cross_Product(ra,normal,&tmp1);
  1294. Matrix3::Rotate_Vector(IInv,tmp1,&tmp2);
  1295. Vector3::Cross_Product(tmp2,ra,&tmp1);
  1296. float den = MassInv + Vector3::Dot_Product(normal,tmp1);
  1297. if (WWMath::Fabs(den) > WWMATH_EPSILON) {
  1298. float mag = num / den;
  1299. *impulse = mag * normal;
  1300. } else {
  1301. WWASSERT(0);
  1302. impulse->Set(0,0,0);
  1303. }
  1304. }
  1305. WWASSERT(WWMath::Is_Valid_Float(impulse->X));
  1306. WWASSERT(WWMath::Is_Valid_Float(impulse->Y));
  1307. WWASSERT(WWMath::Is_Valid_Float(impulse->Z));
  1308. }
  1309. void RigidBodyClass::Assert_State_Valid(void) const
  1310. {
  1311. WWASSERT(State.Position.Is_Valid());
  1312. WWASSERT(State.Orientation.Is_Valid());
  1313. WWASSERT(State.LMomentum.Is_Valid());
  1314. WWASSERT(State.LMomentum.Is_Valid());
  1315. WWASSERT(Velocity.Is_Valid());
  1316. WWASSERT(AngularVelocity.Is_Valid());
  1317. }
  1318. inline void RigidBodyClass::Dump_State(void) const
  1319. {
  1320. WWDEBUG_SAY(("Position: %10.10f , %10.10f , %10.10f \r\n",State.Position.X,State.Position.Y,State.Position.Z));
  1321. WWDEBUG_SAY(("Orientation: %10.10f , %10.10f , %10.10f , %10.10f\r\n",State.Orientation.X,State.Orientation.Y,State.Orientation.Z,State.Orientation.W));
  1322. WWDEBUG_SAY(("LMomentum: %10.10f , %10.10f , %10.10f \r\n",State.LMomentum.X,State.LMomentum.Y,State.LMomentum.Z));
  1323. WWDEBUG_SAY(("AMomentum: %10.10f , %10.10f , %10.10f \r\n",State.AMomentum.X,State.AMomentum.Y,State.AMomentum.Z));
  1324. WWDEBUG_SAY(("ContactBox intersecting: %d\r\n",(ContactBox->Is_Intersecting() ? 1 : 0)));
  1325. WWDEBUG_SAY(("\r\n"));
  1326. }
  1327. void RigidBodyClass::Timestep(float dt)
  1328. {
  1329. WWPROFILE("RigidBody::Timestep");
  1330. LastTimestep = dt;
  1331. // DEBUG DEBUG
  1332. Vector3 vel0 = Velocity;
  1333. Vector3 avel0 = AngularVelocity;
  1334. /*
  1335. ** Update our history buffer
  1336. */
  1337. if (History != NULL) {
  1338. History->Update_History(State,dt);
  1339. /*
  1340. ** Debugging, Draw our history if present
  1341. */
  1342. OBBoxClass box;
  1343. for (int i=1; i<History->History_Count(); i++) {
  1344. const RigidBodyStateStruct & state = History->Get_Historical_State(i);
  1345. ContactBox->Get_Inner_Box(&box,state.Orientation,state.Position);
  1346. DEBUG_RENDER_OBBOX(box,Vector3(0,1.0f,0),0.05f);
  1347. }
  1348. }
  1349. /*
  1350. ** If we have any latency error, attempt to correct it
  1351. */
  1352. Assert_State_Valid();
  1353. if (LatencyError.Position.Length2() > 0.001f) {
  1354. Network_Latency_Error_Correction(dt);
  1355. }
  1356. Assert_State_Valid();
  1357. /*
  1358. ** If we're currently asleep, see if we need to wake up.
  1359. */
  1360. if (Is_Asleep()) {
  1361. if ((Controller != NULL) && (Controller->Is_Inactive() == false)) {
  1362. Set_Flag(ASLEEP,false);
  1363. } else {
  1364. return;
  1365. }
  1366. }
  1367. if (Is_Immovable()) {
  1368. return;
  1369. }
  1370. WWASSERT(Model);
  1371. WWASSERT(ContactBox);
  1372. Inc_Ignore_Counter();
  1373. #if RBODY_DEBUGGING
  1374. if (RBODY_DEBUG_FILTER) {
  1375. WWDEBUG_SAY(("------------------------------\r\n"));
  1376. WWDEBUG_SAY(("RigidBody Timestep Begin (dt=%f).\r\n",dt));
  1377. WWDEBUG_SAY((" Beginning State:\r\n"));
  1378. Dump_State();
  1379. }
  1380. #endif
  1381. /*
  1382. ** Set our active collision region
  1383. */
  1384. Set_Moving_Collision_Region(dt);
  1385. /*
  1386. ** Repeat until we eat all of the time
  1387. */
  1388. const int MAX_COLLISIONS = 10;
  1389. int collisions = 0;
  1390. float remaining_time = dt;
  1391. float timestep;
  1392. while ((remaining_time > 0.0f) && (collisions < MAX_COLLISIONS)) {
  1393. Assert_State_Valid();
  1394. WWPROFILE("RigidBodyClass integration loop");
  1395. timestep = remaining_time;
  1396. /*
  1397. ** Integrate the state of the object
  1398. */
  1399. RigidBodyStateStruct oldstate = State;
  1400. Integrate(timestep);
  1401. /*
  1402. ** Check the final state of the object for collision.
  1403. */
  1404. if (!ContactBox->Is_Intersecting()) {
  1405. /*
  1406. ** Not intersecting so we're accepting the entire move
  1407. */
  1408. remaining_time -= timestep;
  1409. StickCount = 0;
  1410. } else {
  1411. WWPROFILE("Impulsive Collision Handling");
  1412. StickCount++;
  1413. /*
  1414. ** Search for a state where we can get valid contact points
  1415. */
  1416. bool found = Find_State_In_Contact_Zone(oldstate);
  1417. /*
  1418. ** Ad-hoc impact algorithm. Killing angular momentum, clipping
  1419. ** linear momentum to the contact normals.
  1420. */
  1421. State.AMomentum /= (float)StickCount+1;
  1422. if (found) {
  1423. ContactBox->Compute_Contacts();
  1424. if (ContactBox->Get_Contact_Count() > 0) {
  1425. Vector3 contact_centroid(0,0,0);
  1426. for (int ci=0; ci<ContactBox->Get_Contact_Count(); ci++) {
  1427. const CastResultStruct & contact = ContactBox->Get_Contact(ci);
  1428. /*
  1429. ** Eliminate any momentum not tangential to this contact normal
  1430. */
  1431. float dot = Vector3::Dot_Product(State.LMomentum,contact.Normal);
  1432. if (dot < 0.0f) {
  1433. Vector3 impulse = -1.01f * dot * contact.Normal;
  1434. /*
  1435. ** If the object we collided with is a dynamic object, apply an impulse
  1436. ** to it as well.
  1437. */
  1438. PhysClass * other_obj = ContactBox->Peek_Contacted_Object(ci);
  1439. if ((other_obj != NULL) && (other_obj->As_RigidBodyClass() != NULL)) {
  1440. RigidBodyClass * other_rbody = other_obj->As_RigidBodyClass();
  1441. float fraction = Mass / (Mass + other_rbody->Get_Mass());
  1442. State.LMomentum += fraction * impulse;
  1443. other_rbody->Apply_Impulse(-(1.0f - fraction) * impulse, contact.ContactPoint);
  1444. } else if ((other_obj != NULL) && (other_obj->As_Phys3Class() != NULL)) {
  1445. if (!Push_Phys3_Object_Away(other_obj->As_Phys3Class(),contact)) {
  1446. State.LMomentum += impulse;
  1447. }
  1448. remaining_time = 0.0f; // I don't want to resolve a crowd chain reaction all in one frame
  1449. } else {
  1450. State.LMomentum += impulse;
  1451. }
  1452. }
  1453. /*
  1454. ** Accumulate the contact centroid
  1455. */
  1456. contact_centroid += contact.ContactPoint;
  1457. }
  1458. /*
  1459. ** Apply an instantaneous change to the angular momentum to make the physics
  1460. ** feel more realistic. Compute the impluse that was applied to the linear
  1461. ** momentum and apply a fraction of it to the angular momentum at the contact
  1462. ** centroid. (of course, this is all "ad-hoc", not true rigid-body dynamics...)
  1463. */
  1464. Vector3 impulse = 0.3f * (State.LMomentum - oldstate.LMomentum);
  1465. contact_centroid /= ContactBox->Get_Contact_Count();
  1466. Vector3 r = contact_centroid - State.Position;
  1467. Vector3 a_impulse;
  1468. Vector3::Cross_Product(r,impulse,&a_impulse);
  1469. State.AMomentum += a_impulse;
  1470. /*
  1471. ** Done, update the rest of the rigid body state
  1472. */
  1473. Update_Auxiliary_State();
  1474. #if RBODY_DEBUGGING
  1475. if (RBODY_DEBUG_FILTER) {
  1476. WWDEBUG_SAY((" Intersection occured, found state in contact zone.\r\n"));
  1477. WWDEBUG_SAY((" Resulting new state:\r\n"));
  1478. Dump_State();
  1479. }
  1480. #endif
  1481. }
  1482. } else {
  1483. /*
  1484. ** If all else fails:
  1485. ** Fall back to the old, kill the momentum and try to let the
  1486. ** contact spring forces sort it out.
  1487. */
  1488. #if RBODY_DEBUGGING
  1489. if (RBODY_DEBUG_FILTER) {
  1490. WWDEBUG_SAY(("Rigid Body Object: %s is intersecting (%f,%f,%f).\n",Model->Get_Name(),State.Position.X,State.Position.Y,State.Position.Z));
  1491. WWDEBUG_SAY(("Reverting to previous position: (%f,%f,%f)\r\n",oldstate.Position.X,oldstate.Position.Y,oldstate.Position.Z));
  1492. }
  1493. #endif
  1494. State.LMomentum /= (float)(StickCount + 1);
  1495. State.Position = oldstate.Position;
  1496. State.Orientation = oldstate.Orientation;
  1497. remaining_time = 0.0f;
  1498. Update_Auxiliary_State();
  1499. // We've reverted the state, now display the total force and torque that is causing us to "stick"
  1500. #ifdef WWDEBUG
  1501. Vector3 force(0,0,0);
  1502. Vector3 torque(0,0,0);
  1503. Compute_Force_And_Torque(&force,&torque);
  1504. DEBUG_RENDER_VECTOR(State.Position,force,Vector3(0,1,0));
  1505. DEBUG_RENDER_VECTOR(State.Position,force,Vector3(0,0,1));
  1506. #endif
  1507. #if RBODY_DEBUGGING
  1508. if (RBODY_DEBUG_FILTER) {
  1509. WWDEBUG_SAY((" Un-handled intersection! StickCount = %d\r\n",StickCount));
  1510. WWDEBUG_SAY((" Reverted State:\r\n"));
  1511. Dump_State();
  1512. }
  1513. #endif
  1514. }
  1515. }
  1516. collisions++;
  1517. }
  1518. #if 0 // DEBUG DEBUG DEBUG
  1519. if (ContactBox->Is_Intersecting()) {
  1520. State = BackupState;
  1521. goto doitagain;
  1522. }
  1523. #endif
  1524. DEBUG_RENDER_VECTOR(State.Position,Velocity,LMOMENTUM_COLOR);
  1525. DEBUG_RENDER_VECTOR(State.Position,AngularVelocity,AMOMENTUM_COLOR);
  1526. #ifdef WWDEBUG
  1527. if (ContactBox->Is_Intersecting()) {
  1528. OBBoxClass box;
  1529. ContactBox->Get_Inner_Box(&box);
  1530. DEBUG_RENDER_OBBOX(box,Vector3(1,0,0),0.3f);
  1531. }
  1532. #endif
  1533. Dec_Ignore_Counter();
  1534. Model->Set_Transform(Matrix3D(Rotation,State.Position));
  1535. Update_Cull_Box();
  1536. Update_Visibility_Status();
  1537. /*
  1538. ** Release our active collision region
  1539. */
  1540. PhysicsSceneClass::Get_Instance()->Release_Collision_Region();
  1541. /*
  1542. ** See if we can go to sleep and stop simulating!
  1543. */
  1544. if (Can_Go_To_Sleep(dt)) {
  1545. Set_Flag(ASLEEP,true);
  1546. }
  1547. // DEBUG DEBUG
  1548. Vector3 vel_change = Velocity - vel0;
  1549. Vector3 avel_change = AngularVelocity - avel0;
  1550. const float VEL_CHANGE_SPIKE = 10.0f;
  1551. const float AVEL_CHANGE_SPIKE = 10.0f;
  1552. if ( (vel_change.Length() > VEL_CHANGE_SPIKE) ||
  1553. (avel_change.Length() > AVEL_CHANGE_SPIKE))
  1554. {
  1555. WWDEBUG_SAY(("***** Velocity spike detected during RBody::Timestep! "));
  1556. WWDEBUG_SAY(("initial vel: %f final vel: %f\r\n",vel0.Length(),Velocity.Length()));
  1557. WWDEBUG_SAY(("initial avel: %f final avel: %f\r\n",avel0.Length(),AngularVelocity.Length()));
  1558. }
  1559. }
  1560. bool RigidBodyClass::Push_Phys3_Object_Away(Phys3Class * p3obj,const CastResultStruct & contact)
  1561. {
  1562. /*
  1563. ** Notify any observer of the phys3 object that this impact has happened
  1564. */
  1565. CollisionEventClass event;
  1566. event.CollisionResult = &contact;
  1567. event.CollidedRenderObj = NULL;
  1568. event.OtherObj = this;
  1569. p3obj->Collision_Occurred(event);
  1570. /*
  1571. ** Compute a movement vector to push the phy3 object away
  1572. */
  1573. Vector3 move = p3obj->Get_Transform().Get_Translation() - State.Position;
  1574. move.Normalize();
  1575. Vector3 point_vel;
  1576. Compute_Point_Velocity(p3obj->Get_Transform().Get_Translation(),&point_vel);
  1577. float pvel_relative = Vector3::Dot_Product(point_vel,move);
  1578. if (pvel_relative > 0.0f) {
  1579. pvel_relative *= LastTimestep;
  1580. } else {
  1581. pvel_relative = 0.0f;
  1582. }
  1583. move *= ContactBox->Get_Thickness() * ((1.0f - contact.Fraction) + pvel_relative);
  1584. Dec_Ignore_Counter();
  1585. p3obj->Collide(move);
  1586. Inc_Ignore_Counter();
  1587. #if RBODY_DEBUGGING
  1588. if (RBODY_DEBUG_FILTER) {
  1589. WWDEBUG_SAY((" Pushing Phys3 Object %x away (%f, %f, %f)\r\n",p3obj,move.X,move.Y,move.Z));
  1590. }
  1591. #endif
  1592. /*
  1593. ** Check if the object is now out of collision with us. If the external game
  1594. ** logic kills the phys3 object, its collision group will be changed so we check
  1595. ** for that first.
  1596. */
  1597. if (PhysicsSceneClass::Get_Instance()->Do_Groups_Collide(p3obj->Get_Collision_Group(),Get_Collision_Group())) {
  1598. AABoxClass p3box;
  1599. p3obj->Get_Collision_Box(&p3box);
  1600. OBBoxClass rbox;
  1601. ContactBox->Get_Outer_Box(&rbox);
  1602. #if RBODY_DEBUGGING
  1603. if (RBODY_DEBUG_FILTER) {
  1604. WWDEBUG_SAY((" completely clear of our outer collision box\r\n"));
  1605. }
  1606. #endif
  1607. return (CollisionMath::Overlap_Test(rbox,p3box) == CollisionMath::OUTSIDE);
  1608. } else {
  1609. #if RBODY_DEBUGGING
  1610. if (RBODY_DEBUG_FILTER) {
  1611. WWDEBUG_SAY((" still intersecting our outer collision box\r\n"));
  1612. }
  1613. #endif
  1614. return true;
  1615. }
  1616. }
  1617. void RigidBodyClass::Assert_Not_Intersecting(void)
  1618. {
  1619. Inc_Ignore_Counter();
  1620. OBBoxClass obbox = Box->Get_Box();
  1621. CastResultStruct result;
  1622. PhysOBBoxCollisionTestClass test( obbox,
  1623. Vector3(0,0,0),
  1624. &result,
  1625. Get_Collision_Group(),
  1626. COLLISION_TYPE_PHYSICAL);
  1627. PhysicsSceneClass::Get_Instance()->Cast_OBBox(test);
  1628. // WWASSERT(result.StartBad != true);
  1629. if (result.StartBad) {
  1630. WWDEBUG_SAY((" !!!!!!!!!!!!!!!!!!!!!!!!! Rigid Body %s intersecting!\r\n",Model->Get_Name()));
  1631. } else {
  1632. WWDEBUG_SAY((" not intersecting\r\n"));
  1633. }
  1634. Dec_Ignore_Counter();
  1635. }
  1636. void RigidBodyClass::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
  1637. {
  1638. WWASSERT(set_obj_space_box != NULL);
  1639. if (Box) {
  1640. Box->Get_Obj_Space_Bounding_Box(*set_obj_space_box);
  1641. } else {
  1642. MoveablePhysClass::Get_Shadow_Blob_Box(set_obj_space_box);
  1643. }
  1644. }
  1645. bool RigidBodyClass::Find_State_In_Contact_Zone
  1646. (
  1647. const RigidBodyStateStruct & oldstate
  1648. )
  1649. {
  1650. RigidBodyStateStruct state0 = oldstate;
  1651. RigidBodyStateStruct state1 = State;
  1652. RigidBodyStateStruct teststate;
  1653. bool done = false;
  1654. bool success = false;
  1655. int iteration_count = 0;
  1656. const int MAX_ITERATIONS = 5;
  1657. /*
  1658. ** Binary search for a state where the box is close enough to
  1659. ** the terrain to generate contacts but the inner box is not
  1660. ** intersecting.
  1661. */
  1662. while (!done) {
  1663. RigidBodyStateStruct::Lerp(state0,state1,0.5f,&teststate);
  1664. Set_State(teststate);
  1665. if (ContactBox->Is_In_Contact_Zone()) {
  1666. if (ContactBox->Is_Intersecting()) {
  1667. state1 = teststate;
  1668. } else {
  1669. success = true;
  1670. done = true;
  1671. }
  1672. } else {
  1673. state0 = teststate;
  1674. }
  1675. iteration_count++;
  1676. if (iteration_count > MAX_ITERATIONS) {
  1677. done = true;
  1678. }
  1679. }
  1680. return success;
  1681. }
  1682. bool RigidBodyClass::Push(const Vector3 & move)
  1683. {
  1684. Vector3 old_pos = State.Position;
  1685. /*
  1686. ** Create a collision test which sweeps our collision box along the given move vector
  1687. */
  1688. OBBoxClass box = ContactBox->Get_World_Inner_Box();
  1689. CastResultStruct result;
  1690. PhysOBBoxCollisionTestClass test( box,
  1691. move,
  1692. &result,
  1693. Get_Collision_Group(),
  1694. COLLISION_TYPE_PHYSICAL );
  1695. /*
  1696. ** Sweep the box
  1697. */
  1698. Inc_Ignore_Counter();
  1699. PhysicsSceneClass::Get_Instance()->Cast_OBBox(test);
  1700. Dec_Ignore_Counter();
  1701. /*
  1702. ** Move as far as we were allowed
  1703. */
  1704. if (!result.StartBad) {
  1705. if (result.Fraction > 0.0f) {
  1706. State.Position += result.Fraction * move;
  1707. }
  1708. /*
  1709. ** Update the rest of our parameters (TODO: clean this process up)
  1710. */
  1711. Set_Flag(ASLEEP,false);
  1712. Update_Auxiliary_State();
  1713. Update_Visibility_Status();
  1714. Matrix3D m(Rotation,State.Position);
  1715. Model->Set_Transform(m);
  1716. if (ContactBox) {
  1717. ContactBox->Set_Transform(m);
  1718. }
  1719. return (result.Fraction == 1.0f);
  1720. } else {
  1721. return false;
  1722. }
  1723. }
  1724. /***********************************************************************************
  1725. **
  1726. ** Save-Load System
  1727. **
  1728. ***********************************************************************************/
  1729. const PersistFactoryClass & RigidBodyClass::Get_Factory (void) const
  1730. {
  1731. return _RigidBodyFactory;
  1732. }
  1733. bool RigidBodyClass::Save (ChunkSaveClass &csave)
  1734. {
  1735. csave.Begin_Chunk(RBODY_CHUNK_MOVEABLE);
  1736. MoveablePhysClass::Save(csave);
  1737. csave.End_Chunk();
  1738. ODESystemClass * ode_ptr = (ODESystemClass *)this;
  1739. csave.Begin_Chunk(RBODY_CHUNK_VARIABLES);
  1740. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_ODESYSTEM_PTR,ode_ptr);
  1741. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_IBODY,IBody);
  1742. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_IBODYINV,IBodyInv);
  1743. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_POSITION,State.Position);
  1744. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_ORIENTATION,State.Orientation);
  1745. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_LMOMENTUM,State.LMomentum);
  1746. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_AMOMENTUM,State.AMomentum);
  1747. WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_CONTACT_LENGTH,ContactThickness);
  1748. csave.End_Chunk();
  1749. return true;
  1750. }
  1751. bool RigidBodyClass::Load (ChunkLoadClass &cload)
  1752. {
  1753. ODESystemClass * odesys = NULL;
  1754. while (cload.Open_Chunk()) {
  1755. switch(cload.Cur_Chunk_ID())
  1756. {
  1757. case RBODY_CHUNK_MOVEABLE:
  1758. MoveablePhysClass::Load(cload);
  1759. break;
  1760. case RBODY_CHUNK_VARIABLES:
  1761. while (cload.Open_Micro_Chunk()) {
  1762. switch(cload.Cur_Micro_Chunk_ID()) {
  1763. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_ODESYSTEM_PTR,odesys);
  1764. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_IBODY,IBody);
  1765. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_IBODYINV,IBodyInv);
  1766. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_POSITION,State.Position);
  1767. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_ORIENTATION,State.Orientation);
  1768. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_LMOMENTUM,State.LMomentum);
  1769. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_AMOMENTUM,State.AMomentum);
  1770. READ_MICRO_CHUNK(cload,RBODY_VARIABLE_CONTACT_LENGTH,ContactThickness);
  1771. }
  1772. cload.Close_Micro_Chunk();
  1773. }
  1774. break;
  1775. default:
  1776. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  1777. break;
  1778. }
  1779. cload.Close_Chunk();
  1780. }
  1781. if (odesys != NULL) {
  1782. SaveLoadSystemClass::Register_Pointer(odesys,(ODESystemClass *)this);
  1783. }
  1784. SaveLoadSystemClass::Register_Post_Load_Callback(this);
  1785. Update_Auxiliary_State();
  1786. return true;
  1787. }
  1788. void RigidBodyClass::On_Post_Load(void)
  1789. {
  1790. Update_Cached_Model_Parameters();
  1791. }
  1792. /***********************************************************************************************
  1793. **
  1794. ** RigidBodyDefClass Implementation
  1795. **
  1796. ***********************************************************************************************/
  1797. /*
  1798. ** Persist factory for RigidBodyDefClass's
  1799. */
  1800. SimplePersistFactoryClass<RigidBodyDefClass,PHYSICS_CHUNKID_RIGIDBODYDEF> _RigidBodyDefFactory;
  1801. /*
  1802. ** Definition factory for RigidBodyDefClass. This makes it show up in the editor
  1803. */
  1804. DECLARE_DEFINITION_FACTORY(RigidBodyDefClass, CLASSID_RIGIDBODYDEF, "RigidBody") _RigidBodyDefDefFactory;
  1805. /*
  1806. ** Chunk ID's used by RigidBodyDefClass
  1807. */
  1808. enum
  1809. {
  1810. RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF = 0x01106650, // (parent class)
  1811. RIGIDBODYDEF_CHUNK_VARIABLES,
  1812. RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT = 0x00,
  1813. RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,
  1814. };
  1815. RigidBodyDefClass::RigidBodyDefClass(void) :
  1816. AerodynamicDragCoefficient(0.0f),
  1817. CollisionDisabled(false)
  1818. {
  1819. // make our parameters editable
  1820. FLOAT_EDITABLE_PARAM(RigidBodyDefClass, AerodynamicDragCoefficient, 0.0f, 100.0f);
  1821. }
  1822. uint32 RigidBodyDefClass::Get_Class_ID (void) const
  1823. {
  1824. return CLASSID_RIGIDBODYDEF;
  1825. }
  1826. PersistClass * RigidBodyDefClass::Create(void) const
  1827. {
  1828. RigidBodyClass * obj = NEW_REF(RigidBodyClass,());
  1829. obj->Init(*this);
  1830. return obj;
  1831. }
  1832. const PersistFactoryClass & RigidBodyDefClass::Get_Factory (void) const
  1833. {
  1834. return _RigidBodyDefFactory;
  1835. }
  1836. bool RigidBodyDefClass::Save(ChunkSaveClass &csave)
  1837. {
  1838. csave.Begin_Chunk(RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF);
  1839. MoveablePhysDefClass::Save(csave);
  1840. csave.End_Chunk();
  1841. csave.Begin_Chunk(RIGIDBODYDEF_CHUNK_VARIABLES);
  1842. WRITE_MICRO_CHUNK(csave,RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT,AerodynamicDragCoefficient);
  1843. WRITE_MICRO_CHUNK(csave,RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,CollisionDisabled);
  1844. csave.End_Chunk();
  1845. return true;
  1846. }
  1847. bool RigidBodyDefClass::Load(ChunkLoadClass &cload)
  1848. {
  1849. while (cload.Open_Chunk()) {
  1850. switch(cload.Cur_Chunk_ID()) {
  1851. case RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF:
  1852. MoveablePhysDefClass::Load(cload);
  1853. break;
  1854. case RIGIDBODYDEF_CHUNK_VARIABLES:
  1855. while (cload.Open_Micro_Chunk()) {
  1856. switch(cload.Cur_Micro_Chunk_ID()) {
  1857. READ_MICRO_CHUNK(cload,RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT,AerodynamicDragCoefficient);
  1858. READ_MICRO_CHUNK(cload,RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,CollisionDisabled);
  1859. }
  1860. cload.Close_Micro_Chunk();
  1861. }
  1862. break;
  1863. default:
  1864. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  1865. break;
  1866. }
  1867. cload.Close_Chunk();
  1868. }
  1869. return true;
  1870. }
  1871. bool RigidBodyDefClass::Is_Type(const char * type_name)
  1872. {
  1873. if (stricmp(type_name,RigidBodyDefClass::Get_Type_Name()) == 0) {
  1874. return true;
  1875. } else {
  1876. return MoveablePhysDefClass::Is_Type(type_name);
  1877. }
  1878. }