phys3.cpp 111 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731
  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/phys3.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 1/08/02 5:52p $*
  29. * *
  30. * $Revision:: 137 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * Phys3Class::Phys3Class -- Constructor *
  35. * Phys3Class::Phys3Class -- Constructor - from a Definition *
  36. * Phys3Class::~Phys3Class -- Destructor *
  37. * Phys3Class::Get_Bounding_Box -- Returns bounding box of the model *
  38. * Phys3Class::Get_Transform -- Returns the current transform *
  39. * Phys3Class::Set_Transform -- Sets the current transform *
  40. * Phys3Class::Set_Position -- Sets the position *
  41. * Phys3Class::Get_Position -- Returns the current position *
  42. * Phys3Class::Set_Heading -- Set the heading of this object *
  43. * Phys3Class::Get_Heading -- returns the heading of this object *
  44. * Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb *
  45. * Phys3Class::Get_Slide_Angle -- returns the slide angle *
  46. * Phys3Class::Update_Transform -- Recalculate the transform *
  47. * Phys3Class::Set_Model -- Set the model being used *
  48. * Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model *
  49. * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
  50. * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
  51. * Phys3Class::Cast_Ray -- Check a ray for intersection with this object *
  52. * Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj *
  53. * Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj *
  54. * Phys3Class::Intersection_Test -- Check an AABox for intersection with this object *
  55. * Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object *
  56. * Phys3Class::Intersection_Test -- Check a mesh for intersection with this object *
  57. * Phys3Class::Timestep -- Simulate for time dt *
  58. * Phys3Class::Get_Ground_State -- validates and returns the gound state structure *
  59. * Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty *
  60. * Phys3Class::Check_Ground -- Update the ground state *
  61. * Phys3Class::User_Move -- User Move mode. *
  62. * Phys3Class::Ballistic_Move -- Ballistic motion *
  63. * Phys3Class::Slide_Move -- Sliding motion, down a slope *
  64. * Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite friction) *
  65. * Phys3Class::Collide_Move -- Move in response to someone colliding with me *
  66. * Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry *
  67. * Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position *
  68. * Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground *
  69. * Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals *
  70. * Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows *
  71. * Phys3Class::Get_Collision_Box -- returns the current world space collision box *
  72. * Phys3Class::Push -- Move in response to an animated object colliding with me *
  73. * Phys3Class::Collide -- Move in response to a collision *
  74. * Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position *
  75. * Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos *
  76. * Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one *
  77. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  78. #include "phys3.h"
  79. #include "wwdebug.h"
  80. #include "wwhack.h"
  81. #include "wwprofile.h"
  82. #include "wwphystrig.h"
  83. #include "physcoltest.h"
  84. #include "physinttest.h"
  85. #include "colmath.h"
  86. #include "pscene.h"
  87. #include "physcon.h"
  88. #include "physcontrol.h"
  89. #include "bitstream.h"
  90. #include "persistfactory.h"
  91. #include "simpledefinitionfactory.h"
  92. #include "wwphysids.h"
  93. #include "w3d_file.h"
  94. DECLARE_FORCE_LINK(phys3);
  95. const float STEP_HEIGHT = 0.25f;
  96. #define VERBOSE_LOGGING 0
  97. #if VERBOSE_LOGGING
  98. #define VERBOSE_LOG(x) if (/*WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC0) &&*/ (strstr(Model->Get_Name(),"lt"))) { WWDEBUG_SAY(x); }
  99. #else
  100. #define VERBOSE_LOG(x)
  101. #endif
  102. /*
  103. ** Phys3 Networking constants:
  104. */
  105. float Phys3Class::_CorrectionTime = 0.5f;
  106. float Phys3Class::_AllowableError = 0.5f;
  107. float Phys3Class::_PopError = 2.5f;
  108. /*
  109. ** Phys3HistoryClass Parameters
  110. ** Control the implementation of the phys3 history tracking system with the following
  111. ** parameters.
  112. */
  113. const int PHYS3_SNAPSHOT_COUNT = 16; // must be power of 2!
  114. const float PHYS3_HISTORY_MIN_TIME = 1.25f; // seconds of history to store
  115. const int PHYS3_SNAPSHOT_MASK = PHYS3_SNAPSHOT_COUNT - 1;
  116. const float PHYS3_SNAPSHOT_INTERVAL = PHYS3_HISTORY_MIN_TIME / PHYS3_SNAPSHOT_COUNT;
  117. #define PHYS3HISTORY_NO_CORRECTION 0
  118. #define PHYS3HISTORY_ABSOLUTE_CORRECTION 0
  119. #define PHYS3HISTORY_LERP_CORRECTION 1
  120. /**
  121. ** Phys3HistoryClass
  122. ** This class is used to store a history of the state of a Phys3 object. The network
  123. ** update code uses this history to intelligently update the state of the object when
  124. ** a packet is received
  125. */
  126. class Phys3HistoryClass
  127. {
  128. public:
  129. Phys3HistoryClass(void);
  130. ~Phys3HistoryClass(void);
  131. void Init(const Vector3 & pos,const Vector3 & vel);
  132. void Update_History(const Vector3 & pos,const Vector3 & vel, float dt);
  133. void Compute_Old_State(float dt,Vector3 * set_pos,Vector3 * set_vel);
  134. void Apply_Correction(const Vector3 & pos_correction);
  135. int History_Count(void) { return PHYS3_SNAPSHOT_COUNT; }
  136. Vector3 Get_Historical_Position(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)].Position; }
  137. float Find_Time_Of_Nearest_Point(const Vector3 & pos);
  138. void Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point);
  139. private:
  140. int Wrap_Index(int index) { return (index + PHYS3_SNAPSHOT_COUNT) & PHYS3_SNAPSHOT_MASK; }
  141. class StateSnapshotClass
  142. {
  143. public:
  144. StateSnapshotClass(void) : Position(0,0,0), Velocity(0,0,0), Age(0) { }
  145. StateSnapshotClass(const Vector3 & pos,const Vector3 & vel, float age) : Position(pos), Velocity(vel), Age(age) { }
  146. StateSnapshotClass & operator = (const StateSnapshotClass & that) { Position = that.Position; Velocity = that.Velocity; Age = that.Age; }
  147. void Lerp(const StateSnapshotClass & a, const StateSnapshotClass & b, float fraction);
  148. void Update_Age(float dt) { Age += dt; }
  149. Vector3 Position;
  150. Vector3 Velocity;
  151. float Age;
  152. };
  153. bool Initialized;
  154. StateSnapshotClass * SnapshotArray;
  155. int HeadIndex; // history buffer is circular, this is the "head"
  156. };
  157. Phys3HistoryClass::Phys3HistoryClass(void) :
  158. Initialized(false),
  159. SnapshotArray(NULL),
  160. HeadIndex(0)
  161. {
  162. SnapshotArray = new StateSnapshotClass[PHYS3_SNAPSHOT_COUNT];
  163. SnapshotArray[1].Age = 10000.0f;
  164. }
  165. Phys3HistoryClass::~Phys3HistoryClass(void)
  166. {
  167. if (SnapshotArray != NULL) {
  168. delete[] SnapshotArray;
  169. SnapshotArray = NULL;
  170. }
  171. }
  172. void Phys3HistoryClass::Init(const Vector3 & pos,const Vector3 & vel)
  173. {
  174. Initialized = true;
  175. int next_older_index = Wrap_Index(HeadIndex + 1);
  176. SnapshotArray[next_older_index].Age = 1000.0f;
  177. SnapshotArray[next_older_index].Position = pos;
  178. SnapshotArray[next_older_index].Velocity = vel;
  179. }
  180. void Phys3HistoryClass::Update_History(const Vector3 & pos,const Vector3 & vel, float dt)
  181. {
  182. int next_older_index = Wrap_Index(HeadIndex + 1);
  183. /*
  184. ** Special case the first time we insert a state
  185. */
  186. if (SnapshotArray[next_older_index].Age == 0.0f) {
  187. SnapshotArray[next_older_index].Age = 1000.0f;
  188. SnapshotArray[next_older_index].Position = pos;
  189. SnapshotArray[next_older_index].Velocity = vel;
  190. }
  191. /*
  192. ** See if enough time has passed so we need to ratchet forward in the circular buffer
  193. */
  194. if (SnapshotArray[next_older_index].Age + dt > PHYS3_SNAPSHOT_INTERVAL) {
  195. HeadIndex = Wrap_Index(HeadIndex - 1);
  196. SnapshotArray[HeadIndex].Age = 0;
  197. }
  198. SnapshotArray[HeadIndex].Position = pos;
  199. SnapshotArray[HeadIndex].Velocity = vel;
  200. /*
  201. ** Add age to all existing snapshots
  202. */
  203. for (int i=0; i<PHYS3_SNAPSHOT_COUNT; i++) {
  204. if (i != HeadIndex) {
  205. SnapshotArray[i].Update_Age(dt);
  206. }
  207. }
  208. }
  209. void Phys3HistoryClass::Compute_Old_State(float t,Vector3 * set_pos,Vector3 * set_vel)
  210. {
  211. int index = HeadIndex;
  212. bool done = false;
  213. while (!done) {
  214. if (SnapshotArray[index].Age <= t) {
  215. index = Wrap_Index(index + 1);
  216. /*
  217. ** past the end of our history, just return the oldest known state
  218. */
  219. if (index == HeadIndex) {
  220. int tail_index = Wrap_Index(HeadIndex - 1);
  221. if (set_pos != NULL) {
  222. *set_pos = SnapshotArray[tail_index].Position;
  223. }
  224. if (set_vel != NULL) {
  225. *set_vel = SnapshotArray[tail_index].Velocity;
  226. }
  227. return;
  228. }
  229. } else {
  230. done = true;
  231. }
  232. }
  233. int prev_index = Wrap_Index(index - 1);
  234. int next_index = index;
  235. StateSnapshotClass lerp;
  236. float fraction = (t - SnapshotArray[prev_index].Age) / (SnapshotArray[next_index].Age - SnapshotArray[prev_index].Age);
  237. lerp.Lerp(SnapshotArray[prev_index],SnapshotArray[next_index],fraction);
  238. if (set_pos != NULL) {
  239. *set_pos = lerp.Position;
  240. }
  241. if (set_vel != NULL) {
  242. *set_vel = lerp.Velocity;
  243. }
  244. }
  245. void Phys3HistoryClass::Apply_Correction(const Vector3 & pos_correction)
  246. {
  247. #if PHYS3HISTORY_ABSOLUTE_CORRECTION
  248. for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
  249. int index = Wrap_Index(HeadIndex + counter);
  250. SnapshotArray[index].Position += pos_correction;
  251. }
  252. #endif
  253. #if PHYS3HISTORY_LERP_CORRECTION
  254. for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
  255. int index = Wrap_Index(HeadIndex + counter);
  256. float fraction = (PHYS3_HISTORY_MIN_TIME - SnapshotArray[index].Age) / PHYS3_HISTORY_MIN_TIME;
  257. if (fraction > 0.0f) {
  258. SnapshotArray[index].Position += fraction * pos_correction;
  259. }
  260. }
  261. #endif
  262. }
  263. float Phys3HistoryClass::Find_Time_Of_Nearest_Point(const Vector3 & pos)
  264. {
  265. /*
  266. ** Find the nearest line segment
  267. */
  268. float min_dist = 10000.0f;
  269. float time = 0.0f;
  270. for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT-1; counter++) {
  271. int index0 = Wrap_Index(HeadIndex + counter);
  272. int index1 = Wrap_Index(index0 + 1);
  273. LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);
  274. Vector3 point = segment.Find_Point_Closest_To(pos);
  275. float dist = (point - pos).Length();
  276. if (dist < min_dist) {
  277. float fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
  278. min_dist = dist;
  279. time = WWMath::Lerp(SnapshotArray[index0].Age, SnapshotArray[index1].Age, fraction);
  280. }
  281. }
  282. /*
  283. ** Return the age of the nearest point
  284. */
  285. return time;
  286. }
  287. void Phys3HistoryClass::Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point)
  288. {
  289. /*
  290. ** Find the nearest line segment
  291. */
  292. float min_dist = 10000.0f;
  293. for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT-1; counter++) {
  294. int index0 = Wrap_Index(HeadIndex + counter);
  295. int index1 = Wrap_Index(index0 + 1);
  296. LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);
  297. Vector3 point = segment.Find_Point_Closest_To(pos);
  298. float dist = (point - pos).Length();
  299. /*
  300. ** Ignore points with velocity more than 90 deg away from server vel
  301. */
  302. float vdot = 0.0f;
  303. Vector3 history_vel;
  304. float fraction = 0.0f;
  305. if (segment.Get_Length() > 0.0f) {
  306. fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
  307. }
  308. Vector3::Lerp(SnapshotArray[index0].Velocity,SnapshotArray[index1].Velocity,fraction,&history_vel);
  309. vdot = Vector3::Dot_Product(vel,history_vel);
  310. if ((dist < min_dist) && (vdot >= 0.0f)) {
  311. *set_point = point;
  312. min_dist = dist;
  313. }
  314. }
  315. }
  316. void Phys3HistoryClass::StateSnapshotClass::Lerp(const StateSnapshotClass & a, const StateSnapshotClass & b, float fraction)
  317. {
  318. Vector3::Lerp(a.Position,b.Position,fraction,&Position);
  319. Vector3::Lerp(a.Velocity,b.Velocity,fraction,&Velocity);
  320. Age = WWMath::Lerp(a.Age,b.Age,fraction);
  321. }
  322. /***********************************************************************************************
  323. **
  324. ** Phys3Class Implementation
  325. **
  326. ***********************************************************************************************/
  327. /*
  328. ** Declare a PersistFactory for Phys3Classes
  329. */
  330. SimplePersistFactoryClass<Phys3Class,PHYSICS_CHUNKID_PHYS3> _Phys3Factory;
  331. /*
  332. ** Definition factory for Phys3DefClass. This makes it creatable in the editor
  333. */
  334. DECLARE_DEFINITION_FACTORY(Phys3DefClass, CLASSID_PHYS3DEF, "Phys3") _Phys3DefDefFactory;
  335. /*
  336. ** Chunk ID's for Phys3Class
  337. */
  338. enum
  339. {
  340. PHYS3_CHUNK_MOVEABLEPHYS = 0x00483200,
  341. PHYS3_CHUNK_VARIABLES,
  342. PHYS3_VARIABLE_COLLISION_AABOX = 0x00,
  343. PHYS3_VARIABLE_ONGROUND,
  344. PHYS3_VARIABLE_INCOLLISION,
  345. PHYS3_VARIABLE_HEADING,
  346. PHYS3_VARIABLE_NORMSPEED,
  347. PHYS3_VARIABLE_SLIDEANGLE,
  348. PHYS3_VARIABLE_SLIDENORMALZ,
  349. PHYS3_VARIABLE_SLIDEANGLETAN,
  350. PHYS3_VARIABLE_STEPHEIGHT,
  351. PHYS3_VARIABLE_MOVEMODE,
  352. PHYS3_VARIABLE_POSITION,
  353. PHYS3_VARIABLE_VELOCITY
  354. };
  355. static const float DEFAULT_STEP_HEIGHT = 0.25f; // the distance an object will "step up" over an obstacle
  356. static const float DEFAULT_SLIDE_ANGLE = DEG_TO_RADF(45.0f); // steepest angle the character can walk up
  357. static const float DEFAULT_NORMALIZED_SPEED = 10.0f;
  358. static const float GROUND_DISTANCE = 0.1f; // On ground if within this distance
  359. static const float GROUND_EPSILON = (GROUND_DISTANCE) / 5.0f; // Stop at this distance from ground
  360. static const float WALL_EPSILON = 0.5f;//(GROUND_DISTANCE - 0.001f); // Stop at this distance from walls/slides
  361. static const float MIN_STEP_MOVE = 0.25f; // only try to step if we could move this distance
  362. static const float MAX_STEP_MOVE_ANGLE_TAN = 1.0f; // only step if moving at an angle close to the x-y plane
  363. // Debug Vector colors
  364. static const Vector3 VELOCITY_COLOR(1,0,0); // color for the velocity debug vector
  365. static const Vector3 CONTACT_COLOR(0.25f,0.7f,0.2f); // color for contact vectors
  366. static const Vector3 GROUND_COLOR(0.0f,1.0f,1.0f);
  367. static inline void Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move);
  368. /*************************************************************************************
  369. **
  370. ** Phys3Class Implementation
  371. **
  372. *************************************************************************************/
  373. /***********************************************************************************************
  374. * Phys3Class::Phys3Class -- Constructor *
  375. * *
  376. * INPUT: *
  377. * *
  378. * OUTPUT: *
  379. * *
  380. * WARNINGS: *
  381. * *
  382. * HISTORY: *
  383. * 9/15/2000 gth : Created. *
  384. *=============================================================================================*/
  385. Phys3Class::Phys3Class(void)
  386. {
  387. CollisionBox.Center.Set(0,0,1);
  388. CollisionBox.Extent.Set(1,1,1);
  389. OnGround = false;
  390. InCollision = false;
  391. HeadingChanged = false;
  392. GroundSurface = 0;
  393. Heading = 0.0f;
  394. NormSpeed = DEFAULT_NORMALIZED_SPEED;
  395. SlideAngle = DEFAULT_SLIDE_ANGLE;
  396. SlideNormalZ = WWMath::Cos(SlideAngle);
  397. SlideAngleTan = tan(SlideAngle);
  398. StepHeight = DEFAULT_STEP_HEIGHT;
  399. MoveMode = NORMAL_MOVE;
  400. GroundObject = NULL;
  401. AnimationMove.Set(0,0,0);
  402. History = NULL;
  403. LatencyError.Set(0,0,0);
  404. LastKnownPosition.Set(0,0,0);
  405. LastKnownVelocity.Set(0,0,0);
  406. Invalidate_Ground_State();
  407. }
  408. /***********************************************************************************************
  409. * Phys3Class::Init -- initialize from a Definition *
  410. * *
  411. * INPUT: *
  412. * *
  413. * OUTPUT: *
  414. * *
  415. * WARNINGS: *
  416. * *
  417. * HISTORY: *
  418. * 9/15/2000 gth : Created. *
  419. *=============================================================================================*/
  420. void Phys3Class::Init(const Phys3DefClass & def)
  421. {
  422. MoveablePhysClass::Init(def);
  423. CollisionBox.Center.Set(0,0,1);
  424. CollisionBox.Extent.Set(1,1,1);
  425. OnGround = false;
  426. InCollision = false;
  427. GroundSurface = 0;
  428. Heading = 0.0f;
  429. NormSpeed = def.NormSpeed;
  430. SlideAngle = def.SlideAngle;
  431. SlideNormalZ = WWMath::Cos(SlideAngle);
  432. SlideAngleTan = tan(SlideAngle);
  433. StepHeight = def.StepHeight;
  434. MoveMode = NORMAL_MOVE;
  435. GroundObject = NULL;
  436. AnimationMove.Set(0,0,0);
  437. LatencyError.Set(0,0,0);
  438. LastKnownPosition.Set(0,0,0);
  439. LastKnownVelocity.Set(0,0,0);
  440. Update_Cached_Model_Parameters();
  441. Invalidate_Ground_State();
  442. }
  443. /***********************************************************************************************
  444. * Phys3Class::~Phys3Class -- Destructor *
  445. * *
  446. * INPUT: *
  447. * *
  448. * OUTPUT: *
  449. * *
  450. * WARNINGS: *
  451. * *
  452. * HISTORY: *
  453. *=============================================================================================*/
  454. Phys3Class::~Phys3Class(void)
  455. {
  456. if (History != NULL) {
  457. delete History;
  458. }
  459. }
  460. /***********************************************************************************************
  461. * Phys3Class::Get_Bounding_Box -- Returns bounding box of the model *
  462. * *
  463. * INPUT: *
  464. * *
  465. * OUTPUT: *
  466. * *
  467. * WARNINGS: *
  468. * *
  469. * HISTORY: *
  470. * 9/15/2000 gth : Created. *
  471. *=============================================================================================*/
  472. const AABoxClass & Phys3Class::Get_Bounding_Box(void) const
  473. {
  474. assert(Model);
  475. return Model->Get_Bounding_Box();
  476. }
  477. /***********************************************************************************************
  478. * Phys3Class::Get_Transform -- Returns the current transform *
  479. * *
  480. * INPUT: *
  481. * *
  482. * OUTPUT: *
  483. * *
  484. * WARNINGS: *
  485. * *
  486. * HISTORY: *
  487. * 9/15/2000 gth : Created. *
  488. *=============================================================================================*/
  489. const Matrix3D & Phys3Class::Get_Transform(void) const
  490. {
  491. assert(Model);
  492. return Model->Get_Transform();
  493. }
  494. /***********************************************************************************************
  495. * Phys3Class::Set_Transform -- Sets the current transform *
  496. * *
  497. * Note that this "warps" the object to the specified position. The user is responsible *
  498. * for providing a valid position. *
  499. * *
  500. * INPUT: *
  501. * *
  502. * OUTPUT: *
  503. * *
  504. * WARNINGS: *
  505. * *
  506. * HISTORY: *
  507. * 9/15/2000 gth : Created. *
  508. *=============================================================================================*/
  509. void Phys3Class::Set_Transform(const Matrix3D & m)
  510. {
  511. // copy the translation portion of the transform into our state
  512. m.Get_Translation( &State.Position );
  513. #ifdef WWDEBUG
  514. if (!State.Position.Is_Valid()) {
  515. WWDEBUG_SAY(("Phys3Class::Set_Transform got an invalid position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
  516. }
  517. #endif
  518. // copy the Z rotation portion of the transform into our heading variable (ugh...)
  519. Heading = m.Get_Z_Rotation();
  520. Update_Transform();
  521. Update_Cull_Box();
  522. // Wake the object up whenever it moves
  523. Set_Flag(ASLEEP,false);
  524. Invalidate_Ground_State();
  525. Assert_State_Valid();
  526. }
  527. /***********************************************************************************************
  528. * Phys3Class::Set_Position -- Sets the position *
  529. * *
  530. * This blindly warps the object to the given position. The user is responsible for *
  531. * providing a valid position. *
  532. * *
  533. * INPUT: *
  534. * *
  535. * OUTPUT: *
  536. * *
  537. * WARNINGS: *
  538. * *
  539. * HISTORY: *
  540. * 9/16/2000 gth : Created. *
  541. *=============================================================================================*/
  542. void Phys3Class::Set_Position(const Vector3 & position)
  543. {
  544. State.Position = position;
  545. VERBOSE_LOG(("Phys3::Set_Position %s, (%f, %f, %f)\r\n\r\n",Model->Get_Name(),position.X,position.Y,position.Z));
  546. Update_Transform(true);
  547. Update_Cull_Box();
  548. Set_Flag(ASLEEP,false);
  549. Invalidate_Ground_State();
  550. }
  551. /***********************************************************************************************
  552. * Phys3Class::Get_Position -- Returns the current position *
  553. * *
  554. * INPUT: *
  555. * *
  556. * OUTPUT: *
  557. * *
  558. * WARNINGS: *
  559. * *
  560. * HISTORY: *
  561. * 9/16/2000 gth : Created. *
  562. *=============================================================================================*/
  563. const Vector3 & Phys3Class::Get_Position(void) const
  564. {
  565. return State.Position;
  566. }
  567. /***********************************************************************************************
  568. * Phys3Class::Set_Heading -- Set the heading of this object *
  569. * *
  570. * INPUT: *
  571. * *
  572. * OUTPUT: *
  573. * *
  574. * WARNINGS: *
  575. * *
  576. * HISTORY: *
  577. * 9/16/2000 gth : Created. *
  578. *=============================================================================================*/
  579. void Phys3Class::Set_Heading(float heading)
  580. {
  581. WWASSERT(WWMath::Is_Valid_Float(heading));
  582. if (heading != Heading) {
  583. Heading = heading;
  584. HeadingChanged = true;
  585. }
  586. Update_Transform();
  587. }
  588. /***********************************************************************************************
  589. * Phys3Class::Get_Heading -- returns the heading of this object *
  590. * *
  591. * INPUT: *
  592. * *
  593. * OUTPUT: *
  594. * *
  595. * WARNINGS: *
  596. * *
  597. * HISTORY: *
  598. * 9/16/2000 gth : Created. *
  599. *=============================================================================================*/
  600. float Phys3Class::Get_Heading(void) const
  601. {
  602. return Heading;
  603. }
  604. /***********************************************************************************************
  605. * Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb *
  606. * *
  607. * If a phys3 object is resting on a slope greater than this angle, it will slide down the *
  608. * slope. *
  609. * *
  610. * INPUT: *
  611. * *
  612. * OUTPUT: *
  613. * *
  614. * WARNINGS: *
  615. * *
  616. * HISTORY: *
  617. * 9/16/2000 gth : Created. *
  618. *=============================================================================================*/
  619. void Phys3Class::Set_Slide_Angle(float angle)
  620. {
  621. SlideAngle = angle;
  622. SlideNormalZ = WWMath::Cos(angle);
  623. SlideAngleTan = tan(angle);
  624. }
  625. /***********************************************************************************************
  626. * Phys3Class::Get_Slide_Angle -- returns the slide angle *
  627. * *
  628. * INPUT: *
  629. * *
  630. * OUTPUT: *
  631. * *
  632. * WARNINGS: *
  633. * *
  634. * HISTORY: *
  635. * 9/16/2000 gth : Created. *
  636. *=============================================================================================*/
  637. float Phys3Class::Get_Slide_Angle(void) const
  638. {
  639. return SlideAngle;
  640. }
  641. /***********************************************************************************************
  642. * Phys3Class::Update_Transform -- Recalculate the transform *
  643. * *
  644. * INPUT: *
  645. * *
  646. * OUTPUT: *
  647. * *
  648. * WARNINGS: *
  649. * *
  650. * HISTORY: *
  651. * 9/16/2000 gth : Created. *
  652. *=============================================================================================*/
  653. void Phys3Class::Update_Transform(bool position_only)
  654. {
  655. Vector3 pos_dif = Model->Get_Position();
  656. pos_dif -= State.Position;
  657. if ((position_only) && (!HeadingChanged)) {
  658. Model->Set_Position(State.Position);
  659. } else {
  660. Matrix3D tm(1);
  661. tm.Set_Translation(State.Position);
  662. tm.Rotate_Z(Heading);
  663. Model->Set_Transform(tm);
  664. }
  665. if ( pos_dif.Length2() > WWMATH_EPSILON2 ) {
  666. Update_Visibility_Status();
  667. }
  668. #ifdef WWDEBUG
  669. for (int j=0;j<3;j++) {
  670. WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
  671. WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
  672. WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));
  673. WWASSERT(WWMath::Is_Valid_Float(Heading));
  674. }
  675. #endif
  676. HeadingChanged = false;
  677. }
  678. /***********************************************************************************************
  679. * Phys3Class::Set_Model -- Set the model being used *
  680. * *
  681. * INPUT: *
  682. * *
  683. * OUTPUT: *
  684. * *
  685. * WARNINGS: *
  686. * *
  687. * HISTORY: *
  688. * 9/16/2000 gth : Created. *
  689. *=============================================================================================*/
  690. void Phys3Class::Set_Model(RenderObjClass * model)
  691. {
  692. // Let the base class have the model
  693. MoveablePhysClass::Set_Model(model);
  694. // update any members that depend on the model
  695. Update_Cached_Model_Parameters();
  696. // Update our culling box
  697. Update_Cull_Box();
  698. }
  699. /***********************************************************************************************
  700. * Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model *
  701. * *
  702. * INPUT: *
  703. * *
  704. * OUTPUT: *
  705. * *
  706. * WARNINGS: *
  707. * *
  708. * HISTORY: *
  709. * 9/16/2000 gth : Created. *
  710. *=============================================================================================*/
  711. void Phys3Class::Update_Cached_Model_Parameters(void)
  712. {
  713. // if we don't have a model yet, just return
  714. if (Model == NULL) {
  715. return;
  716. } else {
  717. // Construct our collision box from the model
  718. RenderObjClass * box = NULL;
  719. if (Model->Class_ID() == RenderObjClass::CLASSID_DISTLOD) {
  720. RenderObjClass * lod0 = Model->Get_Sub_Object(0);
  721. box = lod0->Get_Sub_Object_By_Name("WORLDBOX");
  722. lod0->Release_Ref();
  723. } else {
  724. box = Model->Get_Sub_Object_By_Name("WORLDBOX");
  725. }
  726. if (box) {
  727. Matrix3D old_transform = Model->Get_Transform();
  728. Model->Set_Transform(Matrix3D(1));
  729. CollisionBox = box->Get_Bounding_Box();
  730. box->Release_Ref();
  731. Model->Set_Transform(old_transform);
  732. }
  733. }
  734. }
  735. /***********************************************************************************************
  736. * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
  737. * *
  738. * As the code comment says... since we changed Phys3 to essentially have infinite friction, *
  739. * this function will only work if the object is in the air currently. *
  740. * *
  741. * INPUT: *
  742. * *
  743. * OUTPUT: *
  744. * *
  745. * WARNINGS: *
  746. * *
  747. * HISTORY: *
  748. * 9/16/2000 gth : Created. *
  749. *=============================================================================================*/
  750. void Phys3Class::Apply_Impulse(const Vector3 & impulse)
  751. {
  752. // Impluse applied to center of mass simply adds to the linear momentum
  753. // NOTE: the current algorithm does not maintain velocity uless undergoing
  754. // ballistic movement... this routine will often have no effect...
  755. State.Velocity += impulse * MassInv;
  756. }
  757. /***********************************************************************************************
  758. * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
  759. * *
  760. * INPUT: *
  761. * *
  762. * OUTPUT: *
  763. * *
  764. * WARNINGS: *
  765. * *
  766. * HISTORY: *
  767. * 9/16/2000 gth : Created. *
  768. *=============================================================================================*/
  769. void Phys3Class::Apply_Impulse(const Vector3 & impulse, const Vector3 & wpos)
  770. {
  771. // Phys3 has only linear momentum so just apply the impulse to the CM...
  772. Apply_Impulse(impulse);
  773. }
  774. /***********************************************************************************************
  775. * Phys3Class::Cast_Ray -- Check a ray for intersection with this object *
  776. * *
  777. * INPUT: *
  778. * *
  779. * OUTPUT: *
  780. * *
  781. * WARNINGS: *
  782. * *
  783. * HISTORY: *
  784. * 9/16/2000 gth : Created. *
  785. *=============================================================================================*/
  786. bool Phys3Class::Cast_Ray(PhysRayCollisionTestClass & raytest)
  787. {
  788. if (Model->Cast_Ray(raytest)) {
  789. raytest.CollidedPhysObj = this;
  790. return true;
  791. }
  792. return false;
  793. }
  794. /***********************************************************************************************
  795. * Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj *
  796. * *
  797. * INPUT: *
  798. * *
  799. * OUTPUT: *
  800. * *
  801. * WARNINGS: *
  802. * *
  803. * HISTORY: *
  804. * 9/16/2000 gth : Created. *
  805. *=============================================================================================*/
  806. bool Phys3Class::Cast_AABox(PhysAABoxCollisionTestClass & boxtest)
  807. {
  808. AABoxClass wrldbox;
  809. wrldbox.Extent = CollisionBox.Extent;
  810. wrldbox.Center = State.Position + CollisionBox.Center;
  811. if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,boxtest.Result)) {
  812. boxtest.CollidedPhysObj = this;
  813. return true;
  814. }
  815. return false;
  816. }
  817. /***********************************************************************************************
  818. * Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj *
  819. * *
  820. * INPUT: *
  821. * *
  822. * OUTPUT: *
  823. * *
  824. * WARNINGS: *
  825. * *
  826. * HISTORY: *
  827. * 9/16/2000 gth : Created. *
  828. *=============================================================================================*/
  829. bool Phys3Class::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest)
  830. {
  831. AABoxClass wrldbox;
  832. wrldbox.Extent = CollisionBox.Extent;
  833. wrldbox.Center = State.Position + CollisionBox.Center;
  834. if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,Vector3(0,0,0),boxtest.Result)) {
  835. boxtest.CollidedPhysObj = this;
  836. return true;
  837. }
  838. return false;
  839. }
  840. /***********************************************************************************************
  841. * Phys3Class::Intersection_Test -- Check an AABox for intersection with this object *
  842. * *
  843. * INPUT: *
  844. * *
  845. * OUTPUT: *
  846. * *
  847. * WARNINGS: *
  848. * *
  849. * HISTORY: *
  850. * 9/16/2000 gth : Created. *
  851. *=============================================================================================*/
  852. bool Phys3Class::Intersection_Test(PhysAABoxIntersectionTestClass & test)
  853. {
  854. AABoxClass wrldbox;
  855. wrldbox.Extent = CollisionBox.Extent;
  856. wrldbox.Center = State.Position + CollisionBox.Center;
  857. if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
  858. test.Add_Intersected_Object(this);
  859. return true;
  860. }
  861. return false;
  862. }
  863. /***********************************************************************************************
  864. * Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object *
  865. * *
  866. * INPUT: *
  867. * *
  868. * OUTPUT: *
  869. * *
  870. * WARNINGS: *
  871. * *
  872. * HISTORY: *
  873. * 9/16/2000 gth : Created. *
  874. *=============================================================================================*/
  875. bool Phys3Class::Intersection_Test(PhysOBBoxIntersectionTestClass & test)
  876. {
  877. AABoxClass wrldbox;
  878. wrldbox.Extent = CollisionBox.Extent;
  879. wrldbox.Center = State.Position + CollisionBox.Center;
  880. if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
  881. test.Add_Intersected_Object(this);
  882. return true;
  883. }
  884. return false;
  885. }
  886. /***********************************************************************************************
  887. * Phys3Class::Intersection_Test -- Check a mesh for intersection with this object *
  888. * *
  889. * INPUT: *
  890. * *
  891. * OUTPUT: *
  892. * *
  893. * WARNINGS: *
  894. * *
  895. * HISTORY: *
  896. * 9/16/2000 gth : Created. *
  897. *=============================================================================================*/
  898. bool Phys3Class::Intersection_Test(PhysMeshIntersectionTestClass & test)
  899. {
  900. AABoxClass wrldbox;
  901. wrldbox.Extent = CollisionBox.Extent;
  902. wrldbox.Center = State.Position + CollisionBox.Center;
  903. AABoxIntersectionTestClass our_test(wrldbox,test.CollisionType);
  904. if (test.Mesh->Intersect_AABox(our_test)) {
  905. test.Add_Intersected_Object(this);
  906. return true;
  907. }
  908. return false;
  909. }
  910. /***********************************************************************************
  911. **
  912. **
  913. ** Simulation Code
  914. **
  915. **
  916. ***********************************************************************************/
  917. /***********************************************************************************************
  918. * Phys3Class::Timestep -- Simulate for time dt *
  919. * *
  920. * INPUT: *
  921. * *
  922. * OUTPUT: *
  923. * *
  924. * WARNINGS: *
  925. * *
  926. * HISTORY: *
  927. * 9/16/2000 gth : Created. *
  928. *=============================================================================================*/
  929. void Phys3Class::Timestep(float dt)
  930. {
  931. WWPROFILE("Phys3::Timestep");
  932. VERBOSE_LOG(("\r\n***** Phys3::Timestep. %s position: %f %f %f\r\n",Model->Get_Name(),State.Position.X,State.Position.Y,State.Position.Z));
  933. /*
  934. ** Update our history buffer
  935. */
  936. if (History != NULL) {
  937. History->Update_History(State.Position,State.Velocity,dt);
  938. /*
  939. ** Debugging, Draw our history if present
  940. */
  941. Vector3 p0 = State.Position;
  942. Vector3 p1;
  943. for (int i=1; i<History->History_Count(); i++) {
  944. p1 = History->Get_Historical_Position(i);
  945. DEBUG_RENDER_VECTOR(p0,p1-p0,VELOCITY_COLOR);
  946. p0 = p1;
  947. }
  948. }
  949. /*
  950. ** If there is remaining latency error, correct some of it
  951. */
  952. if (LatencyError.Length2() > 0.001f) {
  953. Vector3 correction;
  954. float pop_error2 = _PopError * _PopError;
  955. float allowable_error2 = _AllowableError * _AllowableError;
  956. float latency_error2 = LatencyError.Length2();
  957. if (State.Velocity.Length2() > 1.0f) {
  958. allowable_error2 = 0.0f;
  959. }
  960. if (latency_error2 > pop_error2) {
  961. /*
  962. ** Teleport to the network position:
  963. */
  964. Network_Teleport_Correction();
  965. } else if (latency_error2 > allowable_error2) {
  966. /*
  967. ** Try to gracefully correct the error:
  968. */
  969. float frame_time = dt;
  970. float correction_time = _CorrectionTime;
  971. float correction_fraction;
  972. if (correction_time <= frame_time) {
  973. correction_fraction = 1.0f;
  974. } else {
  975. correction_fraction = WWMath::Clamp(frame_time / correction_time);
  976. }
  977. correction_fraction = WWMath::Clamp(correction_fraction,0.0f,0.5f);
  978. correction = correction_fraction * LatencyError;
  979. /*
  980. ** Smoothly correct our position
  981. */
  982. Vector3 start = State.Position;
  983. this->Apply_Move(correction,1.0f,true,false,false);
  984. Update_Transform();
  985. Vector3 delta = State.Position - start;
  986. History->Apply_Correction(delta);
  987. /*
  988. ** If the correction completely fails, teleport
  989. */
  990. float correction_len = correction.Length();
  991. float delta_len = delta.Length();
  992. if ((correction_len > 0.0f) && (delta_len/correction_len < 0.1f)) {
  993. Network_Teleport_Correction();
  994. } else {
  995. LatencyError -= delta;
  996. }
  997. }
  998. }
  999. /*
  1000. ** If we're asleep, short circuit
  1001. */
  1002. if (Is_Asleep() && !Is_User_Control_Enabled()) {
  1003. return;
  1004. }
  1005. Vector3 start_pos = State.Position;
  1006. Vector3 move;
  1007. AABoxClass box;
  1008. CastResultStruct res;
  1009. GroundStateStruct & gs = Get_Ground_State();
  1010. OnGround = gs.OnGround;
  1011. GroundSurface = gs.SurfaceType;
  1012. WWASSERT(GroundSurface >= 0);
  1013. WWASSERT(GroundSurface < SURFACE_TYPE_MAX);
  1014. bool moved = false;
  1015. if (Is_User_Control_Enabled()) {
  1016. MoveMode = USER_OVERRIDE;
  1017. moved = User_Move(dt);
  1018. } else {
  1019. if (!gs.OnGround) {
  1020. MoveMode = BALLISTIC_MOVE;
  1021. moved = Ballistic_Move(dt);
  1022. } else {
  1023. if (gs.Normal.Z < SlideNormalZ) {
  1024. MoveMode = SLIDE_MOVE;
  1025. moved = Slide_Move(gs,dt);
  1026. } else {
  1027. MoveMode = NORMAL_MOVE;
  1028. moved = Normal_Move(gs,dt);
  1029. }
  1030. }
  1031. }
  1032. if ((moved) || (HeadingChanged)) {
  1033. WWPROFILE("Phys3Class::Timestep cleanup");
  1034. Update_Transform(true);
  1035. Update_Cull_Box();
  1036. Add_Animation_Move(State.Position - start_pos);
  1037. if (!OnGround) {
  1038. Link_To_Carrier(NULL);
  1039. }
  1040. }
  1041. if (GroundState.OnDynamicObj) {
  1042. Invalidate_Ground_State();
  1043. Set_Flag(ASLEEP,false);
  1044. }
  1045. // DEBUG_RENDER_VECTOR(State.Position,State.Velocity,VELOCITY_COLOR);
  1046. WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
  1047. WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
  1048. WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));
  1049. WWASSERT(WWMath::Is_Valid_Float(State.Velocity.X));
  1050. WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Y));
  1051. WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Z));
  1052. VERBOSE_LOG((" ***** Phys3::Timestep ended. Position: %f %f %f\r\n\r\n",State.Position.X,State.Position.Y,State.Position.Z));
  1053. }
  1054. /***********************************************************************************************
  1055. * Phys3Class::Get_Ground_State -- validates and returns the gound state structure *
  1056. * *
  1057. * INPUT: *
  1058. * *
  1059. * OUTPUT: *
  1060. * *
  1061. * WARNINGS: *
  1062. * *
  1063. * HISTORY: *
  1064. * 9/16/2000 gth : Created. *
  1065. *=============================================================================================*/
  1066. Phys3Class::GroundStateStruct & Phys3Class::Get_Ground_State(void)
  1067. {
  1068. WWPROFILE("Phys3::Get_Ground_State");
  1069. if (GroundState.IsDirty == true) {
  1070. GroundState.IsDirty = false;
  1071. AABoxClass box;
  1072. Compute_WS_Collision_Box(State,&box);
  1073. Check_Ground(box,&GroundState,GROUND_DISTANCE);
  1074. }
  1075. VERBOSE_LOG((" GS: OnGround = %d Normal = %f %f %f\r\n",GroundState.OnGround,GroundState.Normal.X,GroundState.Normal.Y,GroundState.Normal.Z));
  1076. return GroundState;
  1077. }
  1078. /***********************************************************************************************
  1079. * Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty *
  1080. * *
  1081. * INPUT: *
  1082. * *
  1083. * OUTPUT: *
  1084. * *
  1085. * WARNINGS: *
  1086. * *
  1087. * HISTORY: *
  1088. * 9/16/2000 gth : Created. *
  1089. *=============================================================================================*/
  1090. void Phys3Class::Invalidate_Ground_State(void)
  1091. {
  1092. GroundState.IsDirty = true;
  1093. }
  1094. /***********************************************************************************************
  1095. * Phys3Class::Check_Ground -- Update the ground state *
  1096. * *
  1097. * INPUT: *
  1098. * *
  1099. * OUTPUT: *
  1100. * *
  1101. * WARNINGS: *
  1102. * *
  1103. * HISTORY: *
  1104. * 9/16/2000 gth : Created. *
  1105. *=============================================================================================*/
  1106. void Phys3Class::Check_Ground(const AABoxClass & box,GroundStateStruct * gs,float check_dist)
  1107. {
  1108. WWPROFILE("Check_Ground");
  1109. VERBOSE_LOG(("Phys3::Check_Ground\r\n"));
  1110. CastResultStruct result;
  1111. PhysAABoxCollisionTestClass test( box,
  1112. Vector3(0,0,-check_dist),
  1113. &result,
  1114. Get_Collision_Group(),
  1115. COLLISION_TYPE_PHYSICAL );
  1116. Inc_Ignore_Counter();
  1117. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1118. Dec_Ignore_Counter();
  1119. if (result.Fraction < 1.0f) {
  1120. gs->Init_From_Collision_Result(test,result.Fraction * check_dist);
  1121. if (!result.StartBad) {
  1122. Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);
  1123. DEBUG_RENDER_VECTOR(State.Position,gs->Normal,GROUND_COLOR);
  1124. VERBOSE_LOG((" not intersecting\r\n"));
  1125. }
  1126. } else {
  1127. gs->OnGround = false;
  1128. gs->SurfaceType = SURFACE_TYPE_DEFAULT;
  1129. VERBOSE_LOG((" not on ground!\r\n"));
  1130. }
  1131. if (result.StartBad) {
  1132. DEBUG_RENDER_AABOX(box,Vector3(1,0,0),0.25f);
  1133. VERBOSE_LOG((" INTERSECTING!\r\n"));
  1134. }
  1135. }
  1136. /***********************************************************************************************
  1137. * Phys3Class::User_Move -- User Move mode. *
  1138. * *
  1139. * INPUT: *
  1140. * *
  1141. * OUTPUT: *
  1142. * *
  1143. * WARNINGS: *
  1144. * *
  1145. * HISTORY: *
  1146. * 9/16/2000 gth : Created. *
  1147. *=============================================================================================*/
  1148. bool Phys3Class::User_Move(float dt)
  1149. {
  1150. VERBOSE_LOG(("Phys3::User_Move\r\n"));
  1151. if (Controller) {
  1152. Vector3 move(Controller->Get_Move_Vector());
  1153. move.Rotate_Z(Heading);
  1154. State.Velocity = NormSpeed * move;
  1155. move = State.Velocity * dt;
  1156. return Apply_Move(move,dt);
  1157. }
  1158. return false;
  1159. }
  1160. /***********************************************************************************************
  1161. * Phys3Class::Ballistic_Move -- Ballistic motion *
  1162. * *
  1163. * INPUT: *
  1164. * *
  1165. * OUTPUT: *
  1166. * *
  1167. * WARNINGS: *
  1168. * *
  1169. * HISTORY: *
  1170. * 9/16/2000 gth : Created. *
  1171. *=============================================================================================*/
  1172. bool Phys3Class::Ballistic_Move(float dt)
  1173. {
  1174. // Compute a move vector for the object flying through the air...
  1175. WWPROFILE("Phys3::Ballistic_Move");
  1176. VERBOSE_LOG(("Phys3::Ballistic_Move\r\n"));
  1177. Vector3 move;
  1178. float accel = PhysicsConstants::GravityAcceleration.Z * GravScale;
  1179. Vector3 start_vel = State.Velocity;
  1180. Vector3 start_pos = State.Position;
  1181. move.X = State.Velocity.X * dt;
  1182. move.Y = State.Velocity.Y * dt;
  1183. move.Z = 0.5f * accel * dt * dt + State.Velocity.Z * dt;
  1184. bool moved = Apply_Move(move,dt);
  1185. // Compute X,Y velocities from the actual move that was performed
  1186. State.Velocity.X = (State.Position.X - start_pos.X) / dt;
  1187. State.Velocity.Y = (State.Position.Y - start_pos.Y) / dt;
  1188. // Compute the analytical Z velocity and the ad-hoc Z velocity, the
  1189. // more negative one is the one to keep. What this does is use the
  1190. // analytical velocity unless the character hits a roof.
  1191. // if (CollidedThisFrame) {
  1192. // State.Velocity.Z = (State.Position.Z - start_pos.Z) / dt;
  1193. // } else {
  1194. State.Velocity.Z = start_vel.Z + accel * dt;
  1195. // }
  1196. return moved;
  1197. }
  1198. /***********************************************************************************************
  1199. * Phys3Class::Slide_Move -- Sliding motion, down a slope *
  1200. * *
  1201. * INPUT: *
  1202. * *
  1203. * OUTPUT: *
  1204. * *
  1205. * WARNINGS: *
  1206. * *
  1207. * HISTORY: *
  1208. * 9/16/2000 gth : Created. *
  1209. *=============================================================================================*/
  1210. bool Phys3Class::Slide_Move(const GroundStateStruct & gs,float dt)
  1211. {
  1212. // Compute a move vector which causes the object to slide down the slope...
  1213. WWPROFILE("Phys3::Slide_Move");
  1214. VERBOSE_LOG(("Phys3::Slide_Move\r\n"));
  1215. Vector3 start_pos = State.Position;
  1216. Vector3 move = NormSpeed * dt * gs.Down;
  1217. bool moved = Apply_Move(move,dt,true,false);
  1218. State.Velocity = (State.Position - start_pos) / dt;
  1219. return moved;
  1220. }
  1221. /***********************************************************************************************
  1222. * Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite grip) *
  1223. * *
  1224. * INPUT: *
  1225. * *
  1226. * OUTPUT: *
  1227. * *
  1228. * WARNINGS: *
  1229. * *
  1230. * HISTORY: *
  1231. * 9/16/2000 gth : Created. *
  1232. *=============================================================================================*/
  1233. bool Phys3Class::Normal_Move(const GroundStateStruct & gs,float dt)
  1234. {
  1235. WWPROFILE("Phys3::Normal_Move");
  1236. VERBOSE_LOG(("Phys3::Normal_Move\r\n"));
  1237. // phys3 doesn't move unless on a slope or falling.
  1238. State.Velocity.Set(0,0,0);
  1239. return false;
  1240. }
  1241. /***********************************************************************************************
  1242. * Phys3Class::Collide_Move -- Move in response to someone colliding with me *
  1243. * *
  1244. * INPUT: *
  1245. * *
  1246. * OUTPUT: *
  1247. * *
  1248. * WARNINGS: *
  1249. * *
  1250. * HISTORY: *
  1251. * 9/16/2000 gth : Created. *
  1252. *=============================================================================================*/
  1253. bool Phys3Class::Collide_Move(const Vector3 & requested_move,float dt)
  1254. {
  1255. WWPROFILE("Phys3::Collide_Move");
  1256. VERBOSE_LOG(("Phys3::Collide_Move\r\n"));
  1257. // if we're already being considered in a collision (collisions looped back to us) then return
  1258. if (InCollision) {
  1259. return false;
  1260. }
  1261. InCollision = true;
  1262. MoveMode = COLLIDE_MOVE;
  1263. Vector3 start_position = State.Position;
  1264. bool moved = Apply_Move(requested_move,dt,true,false);
  1265. /*
  1266. ** if we moved and we started on the ground, try to stick to the ground
  1267. */
  1268. if (moved && OnGround) {
  1269. Snap_To_Ground(State.Position - start_position,true);
  1270. }
  1271. InCollision = false;
  1272. State.Velocity = (State.Position - start_position) / dt;
  1273. Add_Animation_Move(State.Position - start_position);
  1274. Update_Transform(true);
  1275. Update_Cull_Box();
  1276. return moved;
  1277. }
  1278. /***********************************************************************************************
  1279. * Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry *
  1280. * *
  1281. * INPUT: *
  1282. * *
  1283. * OUTPUT: *
  1284. * *
  1285. * WARNINGS: *
  1286. * *
  1287. * HISTORY: *
  1288. * 9/16/2000 gth : Created. *
  1289. *=============================================================================================*/
  1290. bool Phys3Class::Apply_Move
  1291. (
  1292. const Vector3 & input_move,
  1293. float dt,
  1294. bool allow_sliding,
  1295. bool allow_stepping,
  1296. bool stop_on_walkable
  1297. )
  1298. {
  1299. WWPROFILE("Phys3::Apply_Move");
  1300. VERBOSE_LOG(("Phys3::Apply_Move: %f %f %f\r\n",input_move.X,input_move.Y,input_move.Z));
  1301. #ifdef WWDEBUG
  1302. if (!input_move.Is_Valid()) {
  1303. WWDEBUG_SAY(("Phys3::Apply_Move called with invalid move vector: %f, %f, %f\r\n",input_move.X,input_move.Y,input_move.Z));
  1304. // WWASSERT(input_move.Is_Valid());
  1305. return false;
  1306. }
  1307. #endif
  1308. /*
  1309. ** Early exit if we're not really moving!
  1310. */
  1311. float move_len = input_move.Length();
  1312. if (move_len < WWMATH_EPSILON) {
  1313. if (GroundState.OnGround) {
  1314. Set_Flag(ASLEEP,true);
  1315. }
  1316. VERBOSE_LOG((" no motion\r\n"));
  1317. return false;
  1318. } else {
  1319. VERBOSE_LOG((" applying move (%f,%f,%f)\r\n",input_move.X,input_move.Y,input_move.Z));
  1320. Set_Flag(ASLEEP,false);
  1321. Invalidate_Ground_State();
  1322. }
  1323. bool moved = false;
  1324. bool done = false;
  1325. bool collided = false;
  1326. const int MAX_CONTACTS = 4;
  1327. const int MAX_ITERATIONS = 4;
  1328. int total_iterations = 0;
  1329. int contact_count = 0;
  1330. Vector3 contacts[MAX_CONTACTS];
  1331. Vector3 start_position = State.Position;
  1332. Vector3 move = input_move;
  1333. Vector3 move_dir = move / move_len;
  1334. bool already_stepped = false;
  1335. Inc_Ignore_Counter();
  1336. /*
  1337. ** Apply Move:
  1338. **
  1339. ** - WHILE (I still have movement to apply)
  1340. ** - sweep our collision box up to the first collision
  1341. ** - IF (I started out intersecting) give up, done
  1342. ** - ELSE
  1343. ** - IF (I was able to take the entire move without collision) accept the move, done
  1344. ** - ELSE
  1345. ** - Accept the portion of the move up to the collision
  1346. ** - Call the collision callbacks
  1347. ** - IF (I have no movement remaining OR I'm not allowed to slide) done
  1348. ** - ELSE
  1349. ** - If (I can push the obstacle out of my way) push obstacle, go back to top of loop
  1350. ** - If (I can shatter this object) shatter it, go back to top of loop
  1351. ** - If (This contact is a slope, and I'm trying to walk up it) modify contact to act like a wall
  1352. ** - Reflect the movement vector so that it does not violate the current contacts
  1353. **
  1354. ** Notes:
  1355. ** - Try to stop movement some distance (COLLISION_EPSILON) away from collisions.
  1356. ** - When approaching slides (slopes that we'll slide off of), try to treat them like walls.
  1357. ** - Can't always treat slides like walls since we can fall onto them or walk onto them from the top...
  1358. ** - Trying to get stepping for free usually by just always bumping the character up before taking his move
  1359. **
  1360. */
  1361. while (!done) {
  1362. if ( ++total_iterations > MAX_ITERATIONS ) {
  1363. VERBOSE_LOG(( "Phys3 Exceeded max iterations!\r\n" ));
  1364. done = true;
  1365. }
  1366. /*
  1367. ** Get the collision box for the object
  1368. */
  1369. AABoxClass box;
  1370. Compute_WS_Collision_Box(State,&box);
  1371. CastResultStruct result;
  1372. PhysAABoxCollisionTestClass test( box,
  1373. move,
  1374. &result,
  1375. Get_Collision_Group(),
  1376. COLLISION_TYPE_PHYSICAL );
  1377. /*
  1378. ** If we're allowed to step up, try to
  1379. */
  1380. if (OnGround && allow_stepping && !already_stepped) {
  1381. already_stepped = true;
  1382. /*
  1383. ** Bump the box up Z by STEP_HEIGHT
  1384. */
  1385. test.Translate(Vector3(0,0,STEP_HEIGHT));
  1386. /*
  1387. ** Sweep the box
  1388. */
  1389. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1390. /*
  1391. ** IF: the 'step' test did not start out intersecting (no low roof) then
  1392. ** warp our position up and continue
  1393. **
  1394. ** ELSE: Since the step test started out intersecting we must be under a low
  1395. ** roof. In this case, perform the move from our original position; no
  1396. ** stepping over obstacles.
  1397. */
  1398. if (!result.StartBad) {
  1399. State.Position.Z += STEP_HEIGHT;
  1400. moved = true;
  1401. } else {
  1402. result.Reset();
  1403. test.Translate(Vector3(0,0,-STEP_HEIGHT));
  1404. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1405. }
  1406. } else {
  1407. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1408. }
  1409. VERBOSE_LOG((" Checking collision results: "));
  1410. if (result.StartBad) {
  1411. //WWDEBUG_WARNING(("result.StartBad\n"));
  1412. //PhysicsSceneClass::Get_Instance()->Add_Debug_AABox(box,Vector3(1,0,0));
  1413. VERBOSE_LOG(("StartBad!\r\n"));
  1414. done = true;
  1415. } else {
  1416. if (result.Fraction == 1.0f) {
  1417. /*
  1418. ** the move was successful, accept it.
  1419. */
  1420. VERBOSE_LOG(("Entire move accepted.\r\n"));
  1421. State.Position += move;
  1422. done = true;
  1423. moved = true;
  1424. } else {
  1425. VERBOSE_LOG(("Collided with %s. normal=(%f,%f,%f)\r\n",test.CollidedRenderObj->Get_Name(),result.Normal.X,result.Normal.Y,result.Normal.Z));
  1426. collided = true;
  1427. /*
  1428. ** How far should we stay away from this obstacle
  1429. */
  1430. float epsilon = GROUND_EPSILON;
  1431. if (result.Normal.Z < SlideNormalZ) { // TODO: base this on surface type?
  1432. epsilon = WALL_EPSILON;
  1433. }
  1434. /*
  1435. ** Try to move up to the obstacle
  1436. */
  1437. float move_len = result.Fraction * move.Length();
  1438. if (move_len > epsilon) {
  1439. move_len -= epsilon;
  1440. Vector3 direction = move;
  1441. direction.Normalize();
  1442. State.Position += move_len * direction;
  1443. move -= result.Fraction * move;
  1444. dt -= result.Fraction * dt;
  1445. moved = true;
  1446. contact_count = 0; // since we moved, reset the contacts
  1447. }
  1448. /*
  1449. ** If there is no time remaining, no movement remaining,
  1450. ** or we're not allowed to "slide" along contacts then we're done
  1451. */
  1452. if ( (dt < WWMATH_EPSILON) ||
  1453. (move.Length2() < WWMATH_EPSILON) ||
  1454. (allow_sliding == false) )
  1455. {
  1456. /*
  1457. ** we're close enough to done
  1458. */
  1459. done = true;
  1460. } else {
  1461. /*
  1462. ** We hit something, call the collision callbacks.
  1463. */
  1464. WWASSERT(test.CollidedPhysObj != NULL);
  1465. CollisionReactionType reaction = COLLISION_REACTION_DEFAULT;
  1466. CollisionEventClass event;
  1467. event.OtherObj = test.CollidedPhysObj;
  1468. reaction |= Collision_Occurred(event);
  1469. event.OtherObj = this;
  1470. reaction |= test.CollidedPhysObj->Collision_Occurred(event);
  1471. if (reaction & COLLISION_REACTION_NO_BOUNCE) {
  1472. /*
  1473. ** If we're instructed to not bounce, just erase the remaining time
  1474. */
  1475. dt = 0.0f;
  1476. } else {
  1477. /*
  1478. ** assume we're going to have to clip the move
  1479. */
  1480. bool clip_move = true;
  1481. /*
  1482. ** PUSH: Try to push the obstacle
  1483. */
  1484. if (clip_move) {
  1485. Phys3Class * p3obj = test.CollidedPhysObj->As_Phys3Class();
  1486. if (p3obj != NULL) {
  1487. /*
  1488. ** Compute a move for the object we're contacting which makes it slide out
  1489. ** of our way if we're moving at an angle to it.
  1490. */
  1491. Vector3 dc;
  1492. Vector3::Subtract(p3obj->Get_Position(),State.Position,&dc);
  1493. dc.Normalize();
  1494. float fraction = 1.0f;
  1495. if ((Mass > WWMATH_EPSILON) && (p3obj->Get_Mass() > WWMATH_EPSILON)) {
  1496. fraction = (Mass / (Mass + p3obj->Get_Mass()));
  1497. }
  1498. dc *= fraction * move.Length();
  1499. Dec_Ignore_Counter();
  1500. if (p3obj->Collide_Move(dc,dt)) {
  1501. clip_move = false;
  1502. }
  1503. Inc_Ignore_Counter();
  1504. }
  1505. }
  1506. /*
  1507. ** SHATTER: If the mesh is shatterable, shatter it. We are doing this so that
  1508. ** shatterable meshes are purely cosmetic for multiplay.
  1509. */
  1510. MeshClass * mesh = NULL;
  1511. if ((test.CollidedRenderObj != NULL) && (test.CollidedRenderObj->Class_ID() == RenderObjClass::CLASSID_MESH)) {
  1512. mesh = (MeshClass*)test.CollidedRenderObj;
  1513. }
  1514. if ( (mesh != NULL) &&
  1515. (mesh->Get_W3D_Flags() & W3D_MESH_FLAG_SHATTERABLE) &&
  1516. (mesh->Is_Not_Hidden_At_All()))
  1517. {
  1518. PhysicsSceneClass::Get_Instance()->Shatter_Mesh( mesh,
  1519. State.Position,
  1520. result.Normal,
  1521. State.Velocity );
  1522. if (Observer != NULL) {
  1523. Observer->Object_Shattered_Something(this,test.CollidedPhysObj,result.SurfaceType);
  1524. }
  1525. clip_move = false;
  1526. }
  1527. /*
  1528. ** CLIP: If the obstacle didn't move out of our way and we didn't step over it,
  1529. ** add the new contact and adjust our move to not hit it again
  1530. */
  1531. if (clip_move) {
  1532. if (contact_count < MAX_CONTACTS) {
  1533. Vector3 normal = result.Normal;
  1534. if ( (normal.Z < SlideNormalZ) &&
  1535. (normal.Z > 0.0f) &&
  1536. (GroundState.OnGround) &&
  1537. (GroundState.Normal.Z > SlideNormalZ) &&
  1538. (MoveMode == BALLISTIC_MOVE))
  1539. {
  1540. normal.Z = 0.0f;
  1541. normal.Normalize();
  1542. }
  1543. contacts[contact_count++] = normal;
  1544. DEBUG_RENDER_VECTOR(State.Position,1.5*normal,CONTACT_COLOR);
  1545. } else {
  1546. VERBOSE_LOG(("exceeded max contacts in Phys3::Apply_Move! position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
  1547. done = true;
  1548. }
  1549. Clip_Move(contacts,contact_count,&move);
  1550. }
  1551. }
  1552. }
  1553. }
  1554. }
  1555. }
  1556. Dec_Ignore_Counter();
  1557. return moved;
  1558. }
  1559. /***********************************************************************************************
  1560. * Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position *
  1561. * *
  1562. * INPUT: *
  1563. * *
  1564. * OUTPUT: *
  1565. * *
  1566. * WARNINGS: *
  1567. * *
  1568. * HISTORY: *
  1569. * 9/16/2000 gth : Created. *
  1570. *=============================================================================================*/
  1571. inline bool Phys3Class::Debug_Verify_Position(void)
  1572. {
  1573. #ifdef VERBOSE_LOGGING
  1574. PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
  1575. AABoxClass box;
  1576. Compute_WS_Collision_Box(State,&box);
  1577. NonRefPhysListClass list;
  1578. PhysAABoxIntersectionTestClass test(box,Get_Collision_Group(),COLLISION_TYPE_PHYSICAL,&list);
  1579. Inc_Ignore_Counter();
  1580. bool intersecting = scene->Intersection_Test(test);
  1581. Dec_Ignore_Counter();
  1582. if (intersecting) {
  1583. VERBOSE_LOG((" %s is intersecting %s!\r\n",Model->Get_Name(),list.Peek_Head()->Peek_Model()->Get_Name()));
  1584. } else {
  1585. VERBOSE_LOG((" %s not intersecting\r\n",Model->Get_Name()));
  1586. }
  1587. return intersecting;
  1588. #else
  1589. return true;
  1590. #endif
  1591. }
  1592. /***********************************************************************************************
  1593. * Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground *
  1594. * *
  1595. * INPUT: *
  1596. * *
  1597. * OUTPUT: *
  1598. * *
  1599. * WARNINGS: *
  1600. * *
  1601. * HISTORY: *
  1602. * 9/16/2000 gth : Created. *
  1603. *=============================================================================================*/
  1604. void Phys3Class::Snap_To_Ground(const Vector3 & actual_move,bool was_stepping)
  1605. {
  1606. if (Get_Flag(ASLEEP)) {
  1607. return;
  1608. }
  1609. VERBOSE_LOG(("Phys3::Snap_To_Ground\r\n"));
  1610. /*
  1611. ** Call this if you started a timestep on the ground and want to stay on the ground...
  1612. ** If we were on the ground, we snap down to the ground to ensure that the object does not
  1613. ** fly unrealistically off edges, etc. The logic works like this:
  1614. ** - the object may have to snap at least back down to the same altitude it started at
  1615. ** - in addition to this, we want to keep the object "stuck" on slopes up to SlideAngle
  1616. ** - so, cast its box down this much: delta_z + delta_xy * tan(SlideAngle)
  1617. ** - if what we hit has a slope that less than SlideAngle, snap down!
  1618. */
  1619. float delta_z = actual_move.Z;
  1620. float delta_xy = WWMath::Sqrt(actual_move.X * actual_move.X + actual_move.Y * actual_move.Y);
  1621. /*
  1622. ** If we don't need to snap down, just return
  1623. */
  1624. if (delta_z + delta_xy > 0.0f) {
  1625. float snap_dist = delta_z + delta_xy * SlideAngleTan + GROUND_DISTANCE;
  1626. Inc_Ignore_Counter();
  1627. AABoxClass box;
  1628. Compute_WS_Collision_Box(State,&box);
  1629. CastResultStruct result;
  1630. PhysAABoxCollisionTestClass test(box,Vector3(0,0,-snap_dist),&result,Get_Collision_Group());
  1631. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1632. if ((result.Fraction > 0.0f) && (result.Fraction <= 1.0f) && (!result.StartBad)) {
  1633. float snap_down = 0;
  1634. if (result.Normal.Z > SlideNormalZ) {
  1635. /*
  1636. ** Case 1: ground is within snap distance and that ground is a walkable slope
  1637. ** Snap down to within GROUND_EPSILON of the ground.
  1638. */
  1639. snap_down = snap_dist * result.Fraction;
  1640. if (snap_down > GROUND_EPSILON) {
  1641. State.Position.Z -= snap_down - GROUND_EPSILON;
  1642. }
  1643. OnGround = true;
  1644. Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);
  1645. DEBUG_RENDER_VECTOR(State.Position,result.Normal,GROUND_COLOR);
  1646. VERBOSE_LOG((" now above walkable slope, snapped down %f\r\n",snap_down - GROUND_EPSILON));
  1647. } else {
  1648. /*
  1649. ** Case 2: ground is within snap distance but it is not a walkable slope
  1650. ** If we were stepping, we should snap down STEP_HEIGHT if we can.
  1651. */
  1652. OnGround = false;
  1653. if (was_stepping) {
  1654. // (gth) just let the character snap down to slopes too!
  1655. //snap_down = WWMath::Min(STEP_HEIGHT,snap_dist * result.Fraction);
  1656. if (snap_down > GROUND_EPSILON) {
  1657. State.Position.Z -= snap_down - GROUND_EPSILON;
  1658. }
  1659. }
  1660. Link_To_Carrier(NULL);
  1661. VERBOSE_LOG((" now above non-walkable slope, snapped only up to step-height %f\r\n",snap_down-GROUND_EPSILON));
  1662. }
  1663. } else {
  1664. /*
  1665. ** Case 3: ground is not within snap distance.
  1666. ** If we were stepping, snap back down the step distance.
  1667. */
  1668. OnGround = false;
  1669. if (was_stepping) {
  1670. State.Position.Z -= STEP_HEIGHT;
  1671. }
  1672. VERBOSE_LOG((" now airborne\r\n"));
  1673. }
  1674. /*
  1675. ** Update the GroundState
  1676. */
  1677. GroundState.Init_From_Collision_Result(test,GROUND_EPSILON);
  1678. Dec_Ignore_Counter();
  1679. }
  1680. }
  1681. /***********************************************************************************************
  1682. * Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals *
  1683. * *
  1684. * INPUT: *
  1685. * *
  1686. * OUTPUT: *
  1687. * *
  1688. * WARNINGS: *
  1689. * *
  1690. * HISTORY: *
  1691. * 9/16/2000 gth : Created. *
  1692. *=============================================================================================*/
  1693. void Phys3Class::Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move)
  1694. {
  1695. WWASSERT(contacts != NULL);
  1696. VERBOSE_LOG(("Phys3::Clip_Move\r\n"));
  1697. #ifdef WWDEBUG
  1698. float move_len2 = move->Length2();
  1699. #endif
  1700. // Loop over all of the contacts.
  1701. // On each one we modify our move vector to parallel that plane.
  1702. // Immediately after each modification, we check if the other planes are a problem.
  1703. // At the first time all planes are satisfied, we're done
  1704. for (int i=0; i<contact_count; i++) {
  1705. #ifdef WWDEBUG
  1706. if (fabs(contacts[i].Length() - 1.0f) > WWMATH_EPSILON) {
  1707. WWDEBUG_SAY(("Bad Contact Normal: %f %f %f Length = %f\r\n",contacts[i].X,contacts[i].Y,contacts[i].Z,contacts[i].Length()));
  1708. }
  1709. #endif
  1710. if (!( fabs(contacts[i].Length() - 1.0f) < WWMATH_EPSILON )) {
  1711. *move = Vector3(0,0,0);
  1712. return;
  1713. }
  1714. VERBOSE_LOG((" normal[%d] = %f %f %f\r\n",i,contacts[i].X,contacts[i].Y,contacts[i].Z));
  1715. // push the velocity a little bit away from the plane
  1716. float dot = Vector3::Dot_Product(*move,contacts[i]);
  1717. if (dot < 0.0f) {
  1718. Vector3 adjustment = 1.01f * dot * contacts[i];
  1719. *move -= adjustment;
  1720. //WWASSERT(Vector3::Dot_Product(*move,contacts[i]) >= 0.0f);
  1721. }
  1722. for (int j=0; j<contact_count; j++) {
  1723. float check = Vector3::Dot_Product(*move,contacts[j]);
  1724. if (check < 0.0f) {
  1725. break; // this contact isn't happy yet... keep choppin.
  1726. }
  1727. }
  1728. if (j == contact_count) {
  1729. break; // all contacts are happy!
  1730. }
  1731. }
  1732. #ifdef WWDEBUG
  1733. float new_move_len2 = move->Length2();
  1734. WWASSERT(new_move_len2 <= move_len2 + WWMATH_EPSILON);
  1735. #endif
  1736. }
  1737. /***********************************************************************************************
  1738. * Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows *
  1739. * *
  1740. * INPUT: *
  1741. * *
  1742. * OUTPUT: *
  1743. * *
  1744. * WARNINGS: *
  1745. * *
  1746. * HISTORY: *
  1747. * 9/16/2000 gth : Created. *
  1748. *=============================================================================================*/
  1749. void Phys3Class::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
  1750. {
  1751. WWASSERT(set_obj_space_box != NULL);
  1752. *set_obj_space_box = CollisionBox;
  1753. }
  1754. /***********************************************************************************************
  1755. * Phys3Class::Get_Collision_Box -- returns the current world space collision box *
  1756. * *
  1757. * INPUT: *
  1758. * *
  1759. * OUTPUT: *
  1760. * *
  1761. * WARNINGS: *
  1762. * *
  1763. * HISTORY: *
  1764. * 1/4/2001 gth : Created. *
  1765. *=============================================================================================*/
  1766. void Phys3Class::Get_Collision_Box(AABoxClass * set_box)
  1767. {
  1768. WWASSERT(set_box != NULL);
  1769. Compute_WS_Collision_Box(State,set_box);
  1770. }
  1771. /***********************************************************************************************
  1772. * Phys3Class::Push -- Move in response to an animated object colliding with me *
  1773. * *
  1774. * This function is called when StaticAnimPhys objects detect a collision. The key is that *
  1775. * we don't want to slide along any contacts; just move as far as we can along the given *
  1776. * move vector. *
  1777. * *
  1778. * INPUT: *
  1779. * *
  1780. * OUTPUT: *
  1781. * *
  1782. * WARNINGS: *
  1783. * *
  1784. * HISTORY: *
  1785. * 9/16/2000 gth : Created. *
  1786. *=============================================================================================*/
  1787. bool Phys3Class::Push(const Vector3 & move)
  1788. {
  1789. Vector3 old_pos = State.Position;
  1790. Apply_Move(move,1.0f,false,false);
  1791. Update_Transform(true);
  1792. Update_Cull_Box();
  1793. Vector3 error = (State.Position - old_pos) - move;
  1794. #if VERBOSE_LOGGING
  1795. Vector3 actual_move = State.Position - old_pos;
  1796. bool success = (State.Position - old_pos) == move;
  1797. VERBOSE_LOG((" Phys3::Push. Apparent success: %d Error: %f %f %f\r\n",success, error.X,error.Y,error.Z));
  1798. #endif
  1799. return (error.Length2() < WWMATH_EPSILON * WWMATH_EPSILON);
  1800. }
  1801. /***********************************************************************************************
  1802. * Phys3Class::Collide -- Move in response to a collision *
  1803. * *
  1804. * This function is similar to the 'Push' function, however the phys3 object is allowed to *
  1805. * slide along geometry in order to get out of the way. *
  1806. * *
  1807. * INPUT: *
  1808. * *
  1809. * OUTPUT: *
  1810. * *
  1811. * WARNINGS: *
  1812. * *
  1813. * HISTORY: *
  1814. * 1/4/2001 gth : Created. *
  1815. *=============================================================================================*/
  1816. bool Phys3Class::Collide(const Vector3 & move)
  1817. {
  1818. Vector3 old_pos = State.Position;
  1819. Collide_Move(move,1.0f);
  1820. return ((State.Position - old_pos) == move);
  1821. }
  1822. /***********************************************************************************************
  1823. * Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position *
  1824. * *
  1825. * INPUT: *
  1826. * *
  1827. * OUTPUT: *
  1828. * *
  1829. * WARNINGS: *
  1830. * *
  1831. * HISTORY: *
  1832. * 9/16/2000 gth : Created. *
  1833. *=============================================================================================*/
  1834. bool Phys3Class::Can_Teleport(const Matrix3D &test_tm, bool check_dyn_only, NonRefPhysListClass * result_list)
  1835. {
  1836. //
  1837. // Determine what the world-space collision box for
  1838. // this object would be at the given transform.
  1839. //
  1840. StateStruct test_state;
  1841. test_state.Position.Set (test_tm.Get_Translation ());
  1842. test_state.Velocity.Set (0, 0, 0);
  1843. AABoxClass collision_box;
  1844. Compute_WS_Collision_Box (test_state, &collision_box);
  1845. //
  1846. // Intersect the box with the world.
  1847. //
  1848. PhysAABoxIntersectionTestClass test(collision_box,
  1849. Get_Collision_Group(),
  1850. COLLISION_TYPE_PHYSICAL,
  1851. result_list);
  1852. if (check_dyn_only) {
  1853. test.CheckStaticObjs = false;
  1854. }
  1855. Inc_Ignore_Counter ();
  1856. bool intersect = PhysicsSceneClass::Get_Instance ()->Intersection_Test(test);
  1857. Dec_Ignore_Counter ();
  1858. return (intersect == false);
  1859. }
  1860. /***********************************************************************************************
  1861. * Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos *
  1862. * *
  1863. * Also returns an alternate position if the object cannot occupy the specified position. *
  1864. * *
  1865. * INPUT: *
  1866. * *
  1867. * OUTPUT: *
  1868. * *
  1869. * WARNINGS: *
  1870. * *
  1871. * HISTORY: *
  1872. * 9/16/2000 gth : Created. *
  1873. *=============================================================================================*/
  1874. bool Phys3Class::Can_Teleport_And_Stand(const Matrix3D &test_tm, Matrix3D *new_tm)
  1875. {
  1876. //
  1877. // Determine what the world-space collision box for
  1878. // this object would be at the given transform.
  1879. //
  1880. StateStruct test_state;
  1881. test_state.Position.Set (test_tm.Get_Translation ());
  1882. test_state.Velocity.Set (0, 0, 0);
  1883. AABoxClass collision_box;
  1884. Compute_WS_Collision_Box (test_state, &collision_box);
  1885. Inc_Ignore_Counter ();
  1886. //
  1887. // Cast a box 'down' from this location.
  1888. // This checks to make sure it doesn't intersect anything at its
  1889. // starting position, and its not off the edge of a cliff
  1890. //
  1891. CastResultStruct result;
  1892. Vector3 move_vector (0, 0, -5.0F);
  1893. PhysAABoxCollisionTestClass test( collision_box,
  1894. move_vector,
  1895. &result,
  1896. 0,
  1897. COLLISION_TYPE_PHYSICAL);
  1898. PhysicsSceneClass::Get_Instance ()->Cast_AABox (test);
  1899. Dec_Ignore_Counter ();
  1900. //
  1901. // Did the box land on a suface we can stand on?
  1902. //
  1903. bool retval = false;
  1904. if ((result.StartBad == false) && ((result.Fraction == 1.0F) || (result.Normal.Z >= SlideNormalZ))) {
  1905. //
  1906. // Calculate a new transform for the object
  1907. //
  1908. Vector3 new_pos = test_tm.Get_Translation () + (result.Fraction * move_vector);
  1909. (*new_tm) = test_tm;
  1910. new_tm->Set_Translation (new_pos);
  1911. retval = true;
  1912. }
  1913. return retval;
  1914. }
  1915. /***********************************************************************************************
  1916. * Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one *
  1917. * *
  1918. * INPUT: *
  1919. * *
  1920. * OUTPUT: *
  1921. * *
  1922. * WARNINGS: *
  1923. * *
  1924. * HISTORY: *
  1925. * 9/16/2000 gth : Created. *
  1926. *=============================================================================================*/
  1927. bool Phys3Class::Find_Teleport_Location
  1928. (
  1929. const Vector3 & start,
  1930. float radius,
  1931. Vector3 * out
  1932. )
  1933. {
  1934. bool retval = false;
  1935. //
  1936. // Test the actual point the caller passed to us.
  1937. //
  1938. Matrix3D initial_tm (start);
  1939. if (Can_Teleport_And_Stand (initial_tm, &initial_tm)) {
  1940. (*out) = initial_tm.Get_Translation ();
  1941. return true;
  1942. }
  1943. const int MAX_ATTEMPTS = 10;
  1944. const float MIN_DIST = 0.1F;
  1945. //
  1946. // Try a number of times to find a valid location
  1947. //
  1948. for (int index = 0; !retval && index < MAX_ATTEMPTS; index ++) {
  1949. //
  1950. // Get a random distance and direction to make the attempt in
  1951. //
  1952. float dist = WWMath::Random_Float (MIN_DIST, radius);
  1953. float angle = WWMath::Random_Float (0, DEG_TO_RADF (360));
  1954. float z_delta = dist * SlideNormalZ;
  1955. //
  1956. // Calculate what the new transform would be at this location
  1957. //
  1958. Vector3 test_pos;
  1959. test_pos.X = start.X + WWMath::Cos (angle) * dist;
  1960. test_pos.Y = start.Y + WWMath::Sin (angle) * dist;
  1961. test_pos.Z = start.Z + z_delta;
  1962. Matrix3D test_tm (test_pos);
  1963. //
  1964. // Do the test to make sure this is a valid teleport location.
  1965. //
  1966. Matrix3D new_tm (1);
  1967. if (Can_Teleport_And_Stand (test_tm, &new_tm)) {
  1968. (*out) = new_tm.Get_Translation ();
  1969. retval = true;
  1970. }
  1971. }
  1972. return retval;
  1973. }
  1974. bool Phys3Class::Can_Move_To(const Matrix3D &new_tm)
  1975. {
  1976. //
  1977. // Do a sweep from our current position to the new position.
  1978. //
  1979. Vector3 start_position = Get_Position();
  1980. Vector3 new_position = new_tm.Get_Translation();
  1981. Vector3 move = new_position - start_position;
  1982. AABoxClass box;
  1983. Compute_WS_Collision_Box(State,&box);
  1984. Inc_Ignore_Counter();
  1985. CastResultStruct result;
  1986. PhysAABoxCollisionTestClass test( box,
  1987. move,
  1988. &result,
  1989. 0,
  1990. COLLISION_TYPE_PHYSICAL );
  1991. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  1992. Dec_Ignore_Counter();
  1993. return (result.Fraction == 1.0f);
  1994. }
  1995. void Phys3Class::Assert_State_Valid(void)
  1996. {
  1997. AABoxClass box;
  1998. Compute_WS_Collision_Box(State,&box);
  1999. CastResultStruct result;
  2000. PhysAABoxCollisionTestClass test( box,
  2001. Vector3(0,0,0),
  2002. &result,
  2003. 0,
  2004. COLLISION_TYPE_PHYSICAL );
  2005. Inc_Ignore_Counter();
  2006. PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
  2007. Dec_Ignore_Counter();
  2008. #if VERBOSE_LOGGING
  2009. if (result.StartBad) {
  2010. VERBOSE_LOG(("Phys3 Intersection! ModelName = %s\r\n",Model->Get_Name()));
  2011. }
  2012. #endif
  2013. WWASSERT(State.Position.Is_Valid());
  2014. WWASSERT(State.Velocity.Is_Valid());
  2015. }
  2016. void Phys3Class::Network_State_Update(const Vector3 & pos,const Vector3 & vel)
  2017. {
  2018. Vector3 delta = pos - State.Position;
  2019. Set_Position(pos);
  2020. Set_Velocity(vel);
  2021. Snap_To_Ground(delta,false);
  2022. Update_Transform( true );
  2023. }
  2024. void Phys3Class::Network_Latency_State_Update(const Vector3 & net_pos,const Vector3 & net_vel)
  2025. {
  2026. DEBUG_RENDER_AABOX(AABoxClass(CollisionBox.Center + net_pos,CollisionBox.Extent),Vector3(0,1,0),0.25f);
  2027. /*
  2028. ** Allocate a history object if needed
  2029. */
  2030. if (History == NULL) {
  2031. History = new Phys3HistoryClass;
  2032. History->Init(net_pos,net_vel);
  2033. }
  2034. WWASSERT(History != NULL);
  2035. /*
  2036. ** Search our history to find the point nearest this server update
  2037. */
  2038. Vector3 old_pos;
  2039. History->Find_Nearest_Point(net_pos,net_vel,&old_pos);
  2040. LatencyError = net_pos - old_pos;
  2041. LastKnownPosition = net_pos;
  2042. LastKnownVelocity = net_vel;
  2043. }
  2044. void Phys3Class::Network_Teleport_Correction(void)
  2045. {
  2046. Vector3 new_pos = LastKnownPosition;
  2047. Vector3 correction = new_pos - State.Position;
  2048. Set_Position(new_pos);
  2049. Set_Velocity(LastKnownVelocity);
  2050. #if 0
  2051. History->Apply_Correction(correction);
  2052. #else
  2053. History->Init(LastKnownPosition,LastKnownVelocity);
  2054. #endif
  2055. LatencyError.Set(0,0,0);
  2056. }
  2057. /*************************************************************************************
  2058. **
  2059. ** Phys3Class Save-Load support
  2060. **
  2061. *************************************************************************************/
  2062. const PersistFactoryClass & Phys3Class::Get_Factory (void) const
  2063. {
  2064. return _Phys3Factory;
  2065. }
  2066. bool Phys3Class::Save (ChunkSaveClass &csave)
  2067. {
  2068. csave.Begin_Chunk(PHYS3_CHUNK_MOVEABLEPHYS);
  2069. MoveablePhysClass::Save(csave);
  2070. csave.End_Chunk();
  2071. csave.Begin_Chunk(PHYS3_CHUNK_VARIABLES);
  2072. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
  2073. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_ONGROUND,OnGround);
  2074. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_INCOLLISION,InCollision);
  2075. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_HEADING,Heading);
  2076. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
  2077. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
  2078. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
  2079. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
  2080. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
  2081. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_MOVEMODE,MoveMode);
  2082. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_POSITION,State.Position);
  2083. WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_VELOCITY,State.Velocity);
  2084. csave.End_Chunk();
  2085. return true;
  2086. }
  2087. bool Phys3Class::Load (ChunkLoadClass &cload)
  2088. {
  2089. while (cload.Open_Chunk()) {
  2090. switch(cload.Cur_Chunk_ID())
  2091. {
  2092. case PHYS3_CHUNK_MOVEABLEPHYS:
  2093. MoveablePhysClass::Load(cload);
  2094. break;
  2095. case PHYS3_CHUNK_VARIABLES:
  2096. while (cload.Open_Micro_Chunk()) {
  2097. switch(cload.Cur_Micro_Chunk_ID()) {
  2098. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
  2099. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_ONGROUND,OnGround);
  2100. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_INCOLLISION,InCollision);
  2101. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_HEADING,Heading);
  2102. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
  2103. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
  2104. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
  2105. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
  2106. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
  2107. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_MOVEMODE,MoveMode);
  2108. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_POSITION,State.Position);
  2109. READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_VELOCITY,State.Velocity);
  2110. }
  2111. cload.Close_Micro_Chunk();
  2112. }
  2113. break;
  2114. default:
  2115. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  2116. break;
  2117. }
  2118. cload.Close_Chunk();
  2119. }
  2120. Invalidate_Ground_State();
  2121. SaveLoadSystemClass::Register_Post_Load_Callback(this);
  2122. return true;
  2123. }
  2124. void Phys3Class::On_Post_Load (void)
  2125. {
  2126. MoveablePhysClass::On_Post_Load();
  2127. Update_Cached_Model_Parameters();
  2128. }
  2129. /*************************************************************************************
  2130. **
  2131. ** Phys3Class::GroundStateStruct Implementation
  2132. **
  2133. *************************************************************************************/
  2134. Phys3Class::GroundStateStruct::GroundStateStruct(void) :
  2135. IsDirty(true),
  2136. OnGround(false),
  2137. OnDynamicObj(false),
  2138. SurfaceType(SURFACE_TYPE_DEFAULT),
  2139. Height(0.0f),
  2140. Normal(0,0,1),
  2141. Down(1,0,0),
  2142. GroundObject(NULL),
  2143. GroundRenderObject(NULL)
  2144. {
  2145. Reset();
  2146. }
  2147. void Phys3Class::GroundStateStruct::Reset(void)
  2148. {
  2149. IsDirty = true;
  2150. OnGround = false;
  2151. OnDynamicObj = false;
  2152. SurfaceType = SURFACE_TYPE_DEFAULT;
  2153. Height = 0.0f;
  2154. Normal.Set(0,0,1);
  2155. Down.Set(1,0,0);
  2156. GroundObject = NULL;
  2157. GroundRenderObject = NULL;
  2158. }
  2159. void Phys3Class::GroundStateStruct::Init_From_Collision_Result(PhysAABoxCollisionTestClass & test,float height)
  2160. {
  2161. Reset();
  2162. IsDirty = false;
  2163. if (test.Result->Fraction < 1.0f) {
  2164. OnGround = true;
  2165. SurfaceType = test.Result->SurfaceType;
  2166. Normal = test.Result->Normal;
  2167. GroundObject = test.CollidedPhysObj;
  2168. GroundRenderObject = test.CollidedRenderObj;
  2169. Height = height;
  2170. if (GroundObject != NULL) {
  2171. if ( (GroundObject->As_HumanPhysClass() != NULL) ||
  2172. (GroundObject->As_RigidBodyClass() != NULL))
  2173. {
  2174. OnDynamicObj = true;
  2175. }
  2176. }
  2177. if (test.Result->StartBad) {
  2178. IsDirty = true;
  2179. Normal.Set(0,0,1);
  2180. // VERBOSE_LOG(("ERROR - intersecting object: %s!\r\n",GroundRenderObject->Get_Name()));
  2181. } else {
  2182. if (Normal.Length2() <= 0.0f) {
  2183. WWDEBUG_SAY(("ERROR - detected non-unit normal!\r\n"));
  2184. }
  2185. // compute the down vector for this plane
  2186. // down = N x -Z x N
  2187. Vector3 tmp;
  2188. Vector3::Cross_Product(Normal,Vector3(0,0,-1),&tmp);
  2189. Vector3::Cross_Product(tmp,Normal,&(Down));
  2190. Down.Normalize();
  2191. // VERBOSE_LOG((" on ground, normal=(%f,%f,%f)sliding=%d\r\n",gs->Normal.X,gs->Normal.Y,gs->Normal.Z,(gs->Normal.Z>SlideNormalZ ? 0 : 1)));
  2192. }
  2193. } else {
  2194. OnGround = false;
  2195. OnDynamicObj = false;
  2196. GroundObject = NULL;
  2197. GroundRenderObject = NULL;
  2198. SurfaceType = SURFACE_TYPE_DEFAULT;
  2199. }
  2200. }
  2201. /***********************************************************************************************
  2202. **
  2203. ** Phys3DefClass Implementation
  2204. **
  2205. ***********************************************************************************************/
  2206. /*
  2207. ** Declare a PersistFactory for Phys3DefClasses
  2208. */
  2209. SimplePersistFactoryClass<Phys3DefClass,PHYSICS_CHUNKID_PHYS3DEF> _Phys3DefFactory;
  2210. /*
  2211. ** Chunk ID's used by MoveablePhysDefClass
  2212. */
  2213. enum
  2214. {
  2215. PHYS3DEF_CHUNK_MOVEABLEPHYSDEF = 0x04486000, // (parent class)
  2216. PHYS3DEF_CHUNK_VARIABLES,
  2217. PHYS3DEF_VARIABLE_NORMSPEED = 0x00,
  2218. PHYS3DEF_VARIABLE_SLIDEANGLE,
  2219. PHYS3DEF_VARIABLE_STEPHEIGHT,
  2220. };
  2221. Phys3DefClass::Phys3DefClass(void) :
  2222. NormSpeed(DEFAULT_NORMALIZED_SPEED),
  2223. SlideAngle(DEFAULT_SLIDE_ANGLE),
  2224. StepHeight(DEFAULT_STEP_HEIGHT)
  2225. {
  2226. // make our parameters editable!
  2227. EDITABLE_PARAM(Phys3DefClass, ParameterClass::TYPE_FLOAT, NormSpeed);
  2228. ANGLE_EDITABLE_PARAM(Phys3DefClass, SlideAngle, DEG_TO_RADF(0.0f), DEG_TO_RADF(90.0f));
  2229. FLOAT_EDITABLE_PARAM(Phys3DefClass, StepHeight, 0.0f, 10.0f);
  2230. }
  2231. const PersistFactoryClass & Phys3DefClass::Get_Factory (void) const
  2232. {
  2233. return _Phys3DefFactory;
  2234. }
  2235. uint32 Phys3DefClass::Get_Class_ID (void) const
  2236. {
  2237. return CLASSID_PHYS3DEF;
  2238. }
  2239. PersistClass * Phys3DefClass::Create(void) const
  2240. {
  2241. Phys3Class * obj = NEW_REF(Phys3Class,());
  2242. obj->Init(*this);
  2243. return obj;
  2244. }
  2245. bool Phys3DefClass::Save(ChunkSaveClass &csave)
  2246. {
  2247. csave.Begin_Chunk(PHYS3DEF_CHUNK_MOVEABLEPHYSDEF);
  2248. MoveablePhysDefClass::Save(csave);
  2249. csave.End_Chunk();
  2250. csave.Begin_Chunk(PHYS3DEF_CHUNK_VARIABLES);
  2251. WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
  2252. WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
  2253. WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
  2254. csave.End_Chunk();
  2255. return true;
  2256. }
  2257. bool Phys3DefClass::Load(ChunkLoadClass &cload)
  2258. {
  2259. while (cload.Open_Chunk()) {
  2260. switch(cload.Cur_Chunk_ID()) {
  2261. case PHYS3DEF_CHUNK_MOVEABLEPHYSDEF:
  2262. MoveablePhysDefClass::Load(cload);
  2263. break;
  2264. case PHYS3DEF_CHUNK_VARIABLES:
  2265. while (cload.Open_Micro_Chunk()) {
  2266. switch(cload.Cur_Micro_Chunk_ID()) {
  2267. READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
  2268. READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
  2269. READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
  2270. }
  2271. cload.Close_Micro_Chunk();
  2272. }
  2273. break;
  2274. default:
  2275. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  2276. break;
  2277. }
  2278. cload.Close_Chunk();
  2279. }
  2280. return true;
  2281. }
  2282. bool Phys3DefClass::Is_Type(const char * type_name)
  2283. {
  2284. if (stricmp(type_name,Phys3DefClass::Get_Type_Name()) == 0) {
  2285. return true;
  2286. } else {
  2287. return MoveablePhysDefClass::Is_Type(type_name);
  2288. }
  2289. }