| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** 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 ***
- ***********************************************************************************************
- * *
- * Project Name : WWPhys *
- * *
- * $Archive:: /Commando/Code/wwphys/phys3.cpp $*
- * *
- * Author:: Greg Hjelstrom *
- * *
- * $Modtime:: 1/08/02 5:52p $*
- * *
- * $Revision:: 137 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * Phys3Class::Phys3Class -- Constructor *
- * Phys3Class::Phys3Class -- Constructor - from a Definition *
- * Phys3Class::~Phys3Class -- Destructor *
- * Phys3Class::Get_Bounding_Box -- Returns bounding box of the model *
- * Phys3Class::Get_Transform -- Returns the current transform *
- * Phys3Class::Set_Transform -- Sets the current transform *
- * Phys3Class::Set_Position -- Sets the position *
- * Phys3Class::Get_Position -- Returns the current position *
- * Phys3Class::Set_Heading -- Set the heading of this object *
- * Phys3Class::Get_Heading -- returns the heading of this object *
- * Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb *
- * Phys3Class::Get_Slide_Angle -- returns the slide angle *
- * Phys3Class::Update_Transform -- Recalculate the transform *
- * Phys3Class::Set_Model -- Set the model being used *
- * Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model *
- * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
- * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
- * Phys3Class::Cast_Ray -- Check a ray for intersection with this object *
- * Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj *
- * Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj *
- * Phys3Class::Intersection_Test -- Check an AABox for intersection with this object *
- * Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object *
- * Phys3Class::Intersection_Test -- Check a mesh for intersection with this object *
- * Phys3Class::Timestep -- Simulate for time dt *
- * Phys3Class::Get_Ground_State -- validates and returns the gound state structure *
- * Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty *
- * Phys3Class::Check_Ground -- Update the ground state *
- * Phys3Class::User_Move -- User Move mode. *
- * Phys3Class::Ballistic_Move -- Ballistic motion *
- * Phys3Class::Slide_Move -- Sliding motion, down a slope *
- * Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite friction) *
- * Phys3Class::Collide_Move -- Move in response to someone colliding with me *
- * Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry *
- * Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position *
- * Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground *
- * Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals *
- * Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows *
- * Phys3Class::Get_Collision_Box -- returns the current world space collision box *
- * Phys3Class::Push -- Move in response to an animated object colliding with me *
- * Phys3Class::Collide -- Move in response to a collision *
- * Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position *
- * Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos *
- * Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "phys3.h"
- #include "wwdebug.h"
- #include "wwhack.h"
- #include "wwprofile.h"
- #include "wwphystrig.h"
- #include "physcoltest.h"
- #include "physinttest.h"
- #include "colmath.h"
- #include "pscene.h"
- #include "physcon.h"
- #include "physcontrol.h"
- #include "bitstream.h"
- #include "persistfactory.h"
- #include "simpledefinitionfactory.h"
- #include "wwphysids.h"
- #include "w3d_file.h"
- DECLARE_FORCE_LINK(phys3);
- const float STEP_HEIGHT = 0.25f;
- #define VERBOSE_LOGGING 0
- #if VERBOSE_LOGGING
- #define VERBOSE_LOG(x) if (/*WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC0) &&*/ (strstr(Model->Get_Name(),"lt"))) { WWDEBUG_SAY(x); }
- #else
- #define VERBOSE_LOG(x)
- #endif
- /*
- ** Phys3 Networking constants:
- */
- float Phys3Class::_CorrectionTime = 0.5f;
- float Phys3Class::_AllowableError = 0.5f;
- float Phys3Class::_PopError = 2.5f;
- /*
- ** Phys3HistoryClass Parameters
- ** Control the implementation of the phys3 history tracking system with the following
- ** parameters.
- */
- const int PHYS3_SNAPSHOT_COUNT = 16; // must be power of 2!
- const float PHYS3_HISTORY_MIN_TIME = 1.25f; // seconds of history to store
- const int PHYS3_SNAPSHOT_MASK = PHYS3_SNAPSHOT_COUNT - 1;
- const float PHYS3_SNAPSHOT_INTERVAL = PHYS3_HISTORY_MIN_TIME / PHYS3_SNAPSHOT_COUNT;
- #define PHYS3HISTORY_NO_CORRECTION 0
- #define PHYS3HISTORY_ABSOLUTE_CORRECTION 0
- #define PHYS3HISTORY_LERP_CORRECTION 1
- /**
- ** Phys3HistoryClass
- ** This class is used to store a history of the state of a Phys3 object. The network
- ** update code uses this history to intelligently update the state of the object when
- ** a packet is received
- */
- class Phys3HistoryClass
- {
- public:
- Phys3HistoryClass(void);
- ~Phys3HistoryClass(void);
- void Init(const Vector3 & pos,const Vector3 & vel);
- void Update_History(const Vector3 & pos,const Vector3 & vel, float dt);
- void Compute_Old_State(float dt,Vector3 * set_pos,Vector3 * set_vel);
- void Apply_Correction(const Vector3 & pos_correction);
- int History_Count(void) { return PHYS3_SNAPSHOT_COUNT; }
- Vector3 Get_Historical_Position(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)].Position; }
- float Find_Time_Of_Nearest_Point(const Vector3 & pos);
- void Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point);
- private:
- int Wrap_Index(int index) { return (index + PHYS3_SNAPSHOT_COUNT) & PHYS3_SNAPSHOT_MASK; }
- class StateSnapshotClass
- {
- public:
- StateSnapshotClass(void) : Position(0,0,0), Velocity(0,0,0), Age(0) { }
- StateSnapshotClass(const Vector3 & pos,const Vector3 & vel, float age) : Position(pos), Velocity(vel), Age(age) { }
- StateSnapshotClass & operator = (const StateSnapshotClass & that) { Position = that.Position; Velocity = that.Velocity; Age = that.Age; }
- void Lerp(const StateSnapshotClass & a, const StateSnapshotClass & b, float fraction);
- void Update_Age(float dt) { Age += dt; }
- Vector3 Position;
- Vector3 Velocity;
- float Age;
- };
- bool Initialized;
- StateSnapshotClass * SnapshotArray;
- int HeadIndex; // history buffer is circular, this is the "head"
- };
- Phys3HistoryClass::Phys3HistoryClass(void) :
- Initialized(false),
- SnapshotArray(NULL),
- HeadIndex(0)
- {
- SnapshotArray = new StateSnapshotClass[PHYS3_SNAPSHOT_COUNT];
- SnapshotArray[1].Age = 10000.0f;
- }
- Phys3HistoryClass::~Phys3HistoryClass(void)
- {
- if (SnapshotArray != NULL) {
- delete[] SnapshotArray;
- SnapshotArray = NULL;
- }
- }
- void Phys3HistoryClass::Init(const Vector3 & pos,const Vector3 & vel)
- {
- Initialized = true;
- int next_older_index = Wrap_Index(HeadIndex + 1);
- SnapshotArray[next_older_index].Age = 1000.0f;
- SnapshotArray[next_older_index].Position = pos;
- SnapshotArray[next_older_index].Velocity = vel;
- }
- void Phys3HistoryClass::Update_History(const Vector3 & pos,const Vector3 & vel, float dt)
- {
- int next_older_index = Wrap_Index(HeadIndex + 1);
- /*
- ** Special case the first time we insert a state
- */
- if (SnapshotArray[next_older_index].Age == 0.0f) {
- SnapshotArray[next_older_index].Age = 1000.0f;
- SnapshotArray[next_older_index].Position = pos;
- SnapshotArray[next_older_index].Velocity = vel;
- }
- /*
- ** See if enough time has passed so we need to ratchet forward in the circular buffer
- */
- if (SnapshotArray[next_older_index].Age + dt > PHYS3_SNAPSHOT_INTERVAL) {
- HeadIndex = Wrap_Index(HeadIndex - 1);
- SnapshotArray[HeadIndex].Age = 0;
- }
- SnapshotArray[HeadIndex].Position = pos;
- SnapshotArray[HeadIndex].Velocity = vel;
- /*
- ** Add age to all existing snapshots
- */
- for (int i=0; i<PHYS3_SNAPSHOT_COUNT; i++) {
- if (i != HeadIndex) {
- SnapshotArray[i].Update_Age(dt);
- }
- }
- }
- void Phys3HistoryClass::Compute_Old_State(float t,Vector3 * set_pos,Vector3 * set_vel)
- {
- int index = HeadIndex;
- bool done = false;
-
- while (!done) {
- if (SnapshotArray[index].Age <= t) {
- index = Wrap_Index(index + 1);
-
- /*
- ** past the end of our history, just return the oldest known state
- */
- if (index == HeadIndex) {
- int tail_index = Wrap_Index(HeadIndex - 1);
- if (set_pos != NULL) {
- *set_pos = SnapshotArray[tail_index].Position;
- }
- if (set_vel != NULL) {
- *set_vel = SnapshotArray[tail_index].Velocity;
- }
- return;
- }
-
- } else {
- done = true;
- }
- }
-
- int prev_index = Wrap_Index(index - 1);
- int next_index = index;
- StateSnapshotClass lerp;
- float fraction = (t - SnapshotArray[prev_index].Age) / (SnapshotArray[next_index].Age - SnapshotArray[prev_index].Age);
- lerp.Lerp(SnapshotArray[prev_index],SnapshotArray[next_index],fraction);
- if (set_pos != NULL) {
- *set_pos = lerp.Position;
- }
- if (set_vel != NULL) {
- *set_vel = lerp.Velocity;
- }
- }
- void Phys3HistoryClass::Apply_Correction(const Vector3 & pos_correction)
- {
- #if PHYS3HISTORY_ABSOLUTE_CORRECTION
- for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
- int index = Wrap_Index(HeadIndex + counter);
- SnapshotArray[index].Position += pos_correction;
- }
- #endif
- #if PHYS3HISTORY_LERP_CORRECTION
- for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
- int index = Wrap_Index(HeadIndex + counter);
-
- float fraction = (PHYS3_HISTORY_MIN_TIME - SnapshotArray[index].Age) / PHYS3_HISTORY_MIN_TIME;
- if (fraction > 0.0f) {
- SnapshotArray[index].Position += fraction * pos_correction;
- }
- }
- #endif
- }
- float Phys3HistoryClass::Find_Time_Of_Nearest_Point(const Vector3 & pos)
- {
- /*
- ** Find the nearest line segment
- */
- float min_dist = 10000.0f;
- float time = 0.0f;
- for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT-1; counter++) {
- int index0 = Wrap_Index(HeadIndex + counter);
- int index1 = Wrap_Index(index0 + 1);
- LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);
- Vector3 point = segment.Find_Point_Closest_To(pos);
- float dist = (point - pos).Length();
-
- if (dist < min_dist) {
- float fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
- min_dist = dist;
- time = WWMath::Lerp(SnapshotArray[index0].Age, SnapshotArray[index1].Age, fraction);
- }
- }
- /*
- ** Return the age of the nearest point
- */
- return time;
- }
- void Phys3HistoryClass::Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point)
- {
- /*
- ** Find the nearest line segment
- */
- float min_dist = 10000.0f;
- for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT-1; counter++) {
- int index0 = Wrap_Index(HeadIndex + counter);
- int index1 = Wrap_Index(index0 + 1);
- LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);
- Vector3 point = segment.Find_Point_Closest_To(pos);
- float dist = (point - pos).Length();
- /*
- ** Ignore points with velocity more than 90 deg away from server vel
- */
- float vdot = 0.0f;
- Vector3 history_vel;
- float fraction = 0.0f;
- if (segment.Get_Length() > 0.0f) {
- fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
- }
- Vector3::Lerp(SnapshotArray[index0].Velocity,SnapshotArray[index1].Velocity,fraction,&history_vel);
- vdot = Vector3::Dot_Product(vel,history_vel);
-
- if ((dist < min_dist) && (vdot >= 0.0f)) {
- *set_point = point;
- min_dist = dist;
- }
- }
- }
- void Phys3HistoryClass::StateSnapshotClass::Lerp(const StateSnapshotClass & a, const StateSnapshotClass & b, float fraction)
- {
- Vector3::Lerp(a.Position,b.Position,fraction,&Position);
- Vector3::Lerp(a.Velocity,b.Velocity,fraction,&Velocity);
- Age = WWMath::Lerp(a.Age,b.Age,fraction);
- }
- /***********************************************************************************************
- **
- ** Phys3Class Implementation
- **
- ***********************************************************************************************/
-
- /*
- ** Declare a PersistFactory for Phys3Classes
- */
- SimplePersistFactoryClass<Phys3Class,PHYSICS_CHUNKID_PHYS3> _Phys3Factory;
- /*
- ** Definition factory for Phys3DefClass. This makes it creatable in the editor
- */
- DECLARE_DEFINITION_FACTORY(Phys3DefClass, CLASSID_PHYS3DEF, "Phys3") _Phys3DefDefFactory;
- /*
- ** Chunk ID's for Phys3Class
- */
- enum
- {
- PHYS3_CHUNK_MOVEABLEPHYS = 0x00483200,
- PHYS3_CHUNK_VARIABLES,
- PHYS3_VARIABLE_COLLISION_AABOX = 0x00,
- PHYS3_VARIABLE_ONGROUND,
- PHYS3_VARIABLE_INCOLLISION,
- PHYS3_VARIABLE_HEADING,
- PHYS3_VARIABLE_NORMSPEED,
- PHYS3_VARIABLE_SLIDEANGLE,
- PHYS3_VARIABLE_SLIDENORMALZ,
- PHYS3_VARIABLE_SLIDEANGLETAN,
- PHYS3_VARIABLE_STEPHEIGHT,
- PHYS3_VARIABLE_MOVEMODE,
- PHYS3_VARIABLE_POSITION,
- PHYS3_VARIABLE_VELOCITY
- };
- static const float DEFAULT_STEP_HEIGHT = 0.25f; // the distance an object will "step up" over an obstacle
- static const float DEFAULT_SLIDE_ANGLE = DEG_TO_RADF(45.0f); // steepest angle the character can walk up
- static const float DEFAULT_NORMALIZED_SPEED = 10.0f;
- static const float GROUND_DISTANCE = 0.1f; // On ground if within this distance
- static const float GROUND_EPSILON = (GROUND_DISTANCE) / 5.0f; // Stop at this distance from ground
- static const float WALL_EPSILON = 0.5f;//(GROUND_DISTANCE - 0.001f); // Stop at this distance from walls/slides
- static const float MIN_STEP_MOVE = 0.25f; // only try to step if we could move this distance
- static const float MAX_STEP_MOVE_ANGLE_TAN = 1.0f; // only step if moving at an angle close to the x-y plane
- // Debug Vector colors
- static const Vector3 VELOCITY_COLOR(1,0,0); // color for the velocity debug vector
- static const Vector3 CONTACT_COLOR(0.25f,0.7f,0.2f); // color for contact vectors
- static const Vector3 GROUND_COLOR(0.0f,1.0f,1.0f);
- static inline void Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move);
- /*************************************************************************************
- **
- ** Phys3Class Implementation
- **
- *************************************************************************************/
- /***********************************************************************************************
- * Phys3Class::Phys3Class -- Constructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/15/2000 gth : Created. *
- *=============================================================================================*/
- Phys3Class::Phys3Class(void)
- {
- CollisionBox.Center.Set(0,0,1);
- CollisionBox.Extent.Set(1,1,1);
- OnGround = false;
- InCollision = false;
- HeadingChanged = false;
- GroundSurface = 0;
- Heading = 0.0f;
- NormSpeed = DEFAULT_NORMALIZED_SPEED;
- SlideAngle = DEFAULT_SLIDE_ANGLE;
- SlideNormalZ = WWMath::Cos(SlideAngle);
- SlideAngleTan = tan(SlideAngle);
- StepHeight = DEFAULT_STEP_HEIGHT;
- MoveMode = NORMAL_MOVE;
- GroundObject = NULL;
- AnimationMove.Set(0,0,0);
- History = NULL;
- LatencyError.Set(0,0,0);
- LastKnownPosition.Set(0,0,0);
- LastKnownVelocity.Set(0,0,0);
- Invalidate_Ground_State();
- }
- /***********************************************************************************************
- * Phys3Class::Init -- initialize from a Definition *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/15/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Init(const Phys3DefClass & def)
- {
- MoveablePhysClass::Init(def);
- CollisionBox.Center.Set(0,0,1);
- CollisionBox.Extent.Set(1,1,1);
- OnGround = false;
- InCollision = false;
- GroundSurface = 0;
- Heading = 0.0f;
- NormSpeed = def.NormSpeed;
- SlideAngle = def.SlideAngle;
- SlideNormalZ = WWMath::Cos(SlideAngle);
- SlideAngleTan = tan(SlideAngle);
- StepHeight = def.StepHeight;
- MoveMode = NORMAL_MOVE;
- GroundObject = NULL;
- AnimationMove.Set(0,0,0);
- LatencyError.Set(0,0,0);
- LastKnownPosition.Set(0,0,0);
- LastKnownVelocity.Set(0,0,0);
- Update_Cached_Model_Parameters();
- Invalidate_Ground_State();
- }
- /***********************************************************************************************
- * Phys3Class::~Phys3Class -- Destructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- *=============================================================================================*/
- Phys3Class::~Phys3Class(void)
- {
- if (History != NULL) {
- delete History;
- }
- }
- /***********************************************************************************************
- * Phys3Class::Get_Bounding_Box -- Returns bounding box of the model *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/15/2000 gth : Created. *
- *=============================================================================================*/
- const AABoxClass & Phys3Class::Get_Bounding_Box(void) const
- {
- assert(Model);
- return Model->Get_Bounding_Box();
- }
- /***********************************************************************************************
- * Phys3Class::Get_Transform -- Returns the current transform *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/15/2000 gth : Created. *
- *=============================================================================================*/
- const Matrix3D & Phys3Class::Get_Transform(void) const
- {
- assert(Model);
- return Model->Get_Transform();
- }
- /***********************************************************************************************
- * Phys3Class::Set_Transform -- Sets the current transform *
- * *
- * Note that this "warps" the object to the specified position. The user is responsible *
- * for providing a valid position. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/15/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Set_Transform(const Matrix3D & m)
- {
- // copy the translation portion of the transform into our state
- m.Get_Translation( &State.Position );
- #ifdef WWDEBUG
- if (!State.Position.Is_Valid()) {
- WWDEBUG_SAY(("Phys3Class::Set_Transform got an invalid position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
- }
- #endif
- // copy the Z rotation portion of the transform into our heading variable (ugh...)
- Heading = m.Get_Z_Rotation();
- Update_Transform();
- Update_Cull_Box();
- // Wake the object up whenever it moves
- Set_Flag(ASLEEP,false);
- Invalidate_Ground_State();
- Assert_State_Valid();
- }
- /***********************************************************************************************
- * Phys3Class::Set_Position -- Sets the position *
- * *
- * This blindly warps the object to the given position. The user is responsible for *
- * providing a valid position. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Set_Position(const Vector3 & position)
- {
- State.Position = position;
- VERBOSE_LOG(("Phys3::Set_Position %s, (%f, %f, %f)\r\n\r\n",Model->Get_Name(),position.X,position.Y,position.Z));
- Update_Transform(true);
- Update_Cull_Box();
- Set_Flag(ASLEEP,false);
- Invalidate_Ground_State();
- }
- /***********************************************************************************************
- * Phys3Class::Get_Position -- Returns the current position *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- const Vector3 & Phys3Class::Get_Position(void) const
- {
- return State.Position;
- }
- /***********************************************************************************************
- * Phys3Class::Set_Heading -- Set the heading of this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Set_Heading(float heading)
- {
- WWASSERT(WWMath::Is_Valid_Float(heading));
- if (heading != Heading) {
- Heading = heading;
- HeadingChanged = true;
- }
- Update_Transform();
- }
- /***********************************************************************************************
- * Phys3Class::Get_Heading -- returns the heading of this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- float Phys3Class::Get_Heading(void) const
- {
- return Heading;
- }
- /***********************************************************************************************
- * Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb *
- * *
- * If a phys3 object is resting on a slope greater than this angle, it will slide down the *
- * slope. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Set_Slide_Angle(float angle)
- {
- SlideAngle = angle;
- SlideNormalZ = WWMath::Cos(angle);
- SlideAngleTan = tan(angle);
- }
- /***********************************************************************************************
- * Phys3Class::Get_Slide_Angle -- returns the slide angle *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- float Phys3Class::Get_Slide_Angle(void) const
- {
- return SlideAngle;
- }
- /***********************************************************************************************
- * Phys3Class::Update_Transform -- Recalculate the transform *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Update_Transform(bool position_only)
- {
- Vector3 pos_dif = Model->Get_Position();
- pos_dif -= State.Position;
- if ((position_only) && (!HeadingChanged)) {
-
- Model->Set_Position(State.Position);
-
- } else {
-
- Matrix3D tm(1);
- tm.Set_Translation(State.Position);
- tm.Rotate_Z(Heading);
- Model->Set_Transform(tm);
- }
- if ( pos_dif.Length2() > WWMATH_EPSILON2 ) {
- Update_Visibility_Status();
- }
- #ifdef WWDEBUG
- for (int j=0;j<3;j++) {
- WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
- WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
- WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));
- WWASSERT(WWMath::Is_Valid_Float(Heading));
- }
- #endif
- HeadingChanged = false;
- }
- /***********************************************************************************************
- * Phys3Class::Set_Model -- Set the model being used *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Set_Model(RenderObjClass * model)
- {
- // Let the base class have the model
- MoveablePhysClass::Set_Model(model);
- // update any members that depend on the model
- Update_Cached_Model_Parameters();
- // Update our culling box
- Update_Cull_Box();
- }
- /***********************************************************************************************
- * Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Update_Cached_Model_Parameters(void)
- {
- // if we don't have a model yet, just return
- if (Model == NULL) {
-
- return;
- } else {
- // Construct our collision box from the model
- RenderObjClass * box = NULL;
- if (Model->Class_ID() == RenderObjClass::CLASSID_DISTLOD) {
- RenderObjClass * lod0 = Model->Get_Sub_Object(0);
- box = lod0->Get_Sub_Object_By_Name("WORLDBOX");
- lod0->Release_Ref();
- } else {
- box = Model->Get_Sub_Object_By_Name("WORLDBOX");
- }
-
- if (box) {
- Matrix3D old_transform = Model->Get_Transform();
- Model->Set_Transform(Matrix3D(1));
- CollisionBox = box->Get_Bounding_Box();
- box->Release_Ref();
- Model->Set_Transform(old_transform);
- }
- }
- }
- /***********************************************************************************************
- * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
- * *
- * As the code comment says... since we changed Phys3 to essentially have infinite friction, *
- * this function will only work if the object is in the air currently. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Apply_Impulse(const Vector3 & impulse)
- {
- // Impluse applied to center of mass simply adds to the linear momentum
- // NOTE: the current algorithm does not maintain velocity uless undergoing
- // ballistic movement... this routine will often have no effect...
- State.Velocity += impulse * MassInv;
- }
- /***********************************************************************************************
- * Phys3Class::Apply_Impulse -- Apply an impulse to this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Apply_Impulse(const Vector3 & impulse, const Vector3 & wpos)
- {
- // Phys3 has only linear momentum so just apply the impulse to the CM...
- Apply_Impulse(impulse);
- }
- /***********************************************************************************************
- * Phys3Class::Cast_Ray -- Check a ray for intersection with this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Cast_Ray(PhysRayCollisionTestClass & raytest)
- {
- if (Model->Cast_Ray(raytest)) {
- raytest.CollidedPhysObj = this;
- return true;
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Cast_AABox(PhysAABoxCollisionTestClass & boxtest)
- {
- AABoxClass wrldbox;
- wrldbox.Extent = CollisionBox.Extent;
- wrldbox.Center = State.Position + CollisionBox.Center;
- if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,boxtest.Result)) {
- boxtest.CollidedPhysObj = this;
- return true;
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest)
- {
- AABoxClass wrldbox;
- wrldbox.Extent = CollisionBox.Extent;
- wrldbox.Center = State.Position + CollisionBox.Center;
- if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,Vector3(0,0,0),boxtest.Result)) {
- boxtest.CollidedPhysObj = this;
- return true;
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Intersection_Test -- Check an AABox for intersection with this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Intersection_Test(PhysAABoxIntersectionTestClass & test)
- {
- AABoxClass wrldbox;
- wrldbox.Extent = CollisionBox.Extent;
- wrldbox.Center = State.Position + CollisionBox.Center;
- if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
- test.Add_Intersected_Object(this);
- return true;
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Intersection_Test(PhysOBBoxIntersectionTestClass & test)
- {
- AABoxClass wrldbox;
- wrldbox.Extent = CollisionBox.Extent;
- wrldbox.Center = State.Position + CollisionBox.Center;
- if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
- test.Add_Intersected_Object(this);
- return true;
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Intersection_Test -- Check a mesh for intersection with this object *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Intersection_Test(PhysMeshIntersectionTestClass & test)
- {
- AABoxClass wrldbox;
- wrldbox.Extent = CollisionBox.Extent;
- wrldbox.Center = State.Position + CollisionBox.Center;
- AABoxIntersectionTestClass our_test(wrldbox,test.CollisionType);
- if (test.Mesh->Intersect_AABox(our_test)) {
- test.Add_Intersected_Object(this);
- return true;
- }
- return false;
- }
- /***********************************************************************************
- **
- **
- ** Simulation Code
- **
- **
- ***********************************************************************************/
- /***********************************************************************************************
- * Phys3Class::Timestep -- Simulate for time dt *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Timestep(float dt)
- {
- WWPROFILE("Phys3::Timestep");
- 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));
- /*
- ** Update our history buffer
- */
- if (History != NULL) {
- History->Update_History(State.Position,State.Velocity,dt);
- /*
- ** Debugging, Draw our history if present
- */
- Vector3 p0 = State.Position;
- Vector3 p1;
-
- for (int i=1; i<History->History_Count(); i++) {
- p1 = History->Get_Historical_Position(i);
- DEBUG_RENDER_VECTOR(p0,p1-p0,VELOCITY_COLOR);
- p0 = p1;
- }
- }
- /*
- ** If there is remaining latency error, correct some of it
- */
- if (LatencyError.Length2() > 0.001f) {
- Vector3 correction;
- float pop_error2 = _PopError * _PopError;
- float allowable_error2 = _AllowableError * _AllowableError;
- float latency_error2 = LatencyError.Length2();
- if (State.Velocity.Length2() > 1.0f) {
- allowable_error2 = 0.0f;
- }
- if (latency_error2 > pop_error2) {
-
- /*
- ** Teleport to the network position:
- */
- Network_Teleport_Correction();
- } else if (latency_error2 > allowable_error2) {
- /*
- ** Try to gracefully correct the error:
- */
- float frame_time = dt;
- float correction_time = _CorrectionTime;
- float correction_fraction;
-
- if (correction_time <= frame_time) {
- correction_fraction = 1.0f;
- } else {
- correction_fraction = WWMath::Clamp(frame_time / correction_time);
- }
- correction_fraction = WWMath::Clamp(correction_fraction,0.0f,0.5f);
- correction = correction_fraction * LatencyError;
- /*
- ** Smoothly correct our position
- */
- Vector3 start = State.Position;
- this->Apply_Move(correction,1.0f,true,false,false);
- Update_Transform();
- Vector3 delta = State.Position - start;
- History->Apply_Correction(delta);
- /*
- ** If the correction completely fails, teleport
- */
- float correction_len = correction.Length();
- float delta_len = delta.Length();
- if ((correction_len > 0.0f) && (delta_len/correction_len < 0.1f)) {
-
- Network_Teleport_Correction();
- } else {
- LatencyError -= delta;
- }
- }
- }
- /*
- ** If we're asleep, short circuit
- */
- if (Is_Asleep() && !Is_User_Control_Enabled()) {
- return;
- }
- Vector3 start_pos = State.Position;
- Vector3 move;
- AABoxClass box;
- CastResultStruct res;
- GroundStateStruct & gs = Get_Ground_State();
- OnGround = gs.OnGround;
- GroundSurface = gs.SurfaceType;
- WWASSERT(GroundSurface >= 0);
- WWASSERT(GroundSurface < SURFACE_TYPE_MAX);
- bool moved = false;
- if (Is_User_Control_Enabled()) {
- MoveMode = USER_OVERRIDE;
- moved = User_Move(dt);
- } else {
- if (!gs.OnGround) {
- MoveMode = BALLISTIC_MOVE;
- moved = Ballistic_Move(dt);
- } else {
-
- if (gs.Normal.Z < SlideNormalZ) {
- MoveMode = SLIDE_MOVE;
- moved = Slide_Move(gs,dt);
- } else {
- MoveMode = NORMAL_MOVE;
- moved = Normal_Move(gs,dt);
- }
- }
- }
-
- if ((moved) || (HeadingChanged)) {
- WWPROFILE("Phys3Class::Timestep cleanup");
- Update_Transform(true);
- Update_Cull_Box();
- Add_Animation_Move(State.Position - start_pos);
- if (!OnGround) {
- Link_To_Carrier(NULL);
- }
- }
- if (GroundState.OnDynamicObj) {
- Invalidate_Ground_State();
- Set_Flag(ASLEEP,false);
- }
- // DEBUG_RENDER_VECTOR(State.Position,State.Velocity,VELOCITY_COLOR);
- WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
- WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
- WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));
- WWASSERT(WWMath::Is_Valid_Float(State.Velocity.X));
- WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Y));
- WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Z));
- VERBOSE_LOG((" ***** Phys3::Timestep ended. Position: %f %f %f\r\n\r\n",State.Position.X,State.Position.Y,State.Position.Z));
- }
- /***********************************************************************************************
- * Phys3Class::Get_Ground_State -- validates and returns the gound state structure *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- Phys3Class::GroundStateStruct & Phys3Class::Get_Ground_State(void)
- {
- WWPROFILE("Phys3::Get_Ground_State");
- if (GroundState.IsDirty == true) {
- GroundState.IsDirty = false;
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- Check_Ground(box,&GroundState,GROUND_DISTANCE);
- }
- VERBOSE_LOG((" GS: OnGround = %d Normal = %f %f %f\r\n",GroundState.OnGround,GroundState.Normal.X,GroundState.Normal.Y,GroundState.Normal.Z));
- return GroundState;
- }
- /***********************************************************************************************
- * Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Invalidate_Ground_State(void)
- {
- GroundState.IsDirty = true;
- }
- /***********************************************************************************************
- * Phys3Class::Check_Ground -- Update the ground state *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Check_Ground(const AABoxClass & box,GroundStateStruct * gs,float check_dist)
- {
- WWPROFILE("Check_Ground");
- VERBOSE_LOG(("Phys3::Check_Ground\r\n"));
- CastResultStruct result;
- PhysAABoxCollisionTestClass test( box,
- Vector3(0,0,-check_dist),
- &result,
- Get_Collision_Group(),
- COLLISION_TYPE_PHYSICAL );
- Inc_Ignore_Counter();
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
- Dec_Ignore_Counter();
- if (result.Fraction < 1.0f) {
- gs->Init_From_Collision_Result(test,result.Fraction * check_dist);
-
- if (!result.StartBad) {
- Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);
- DEBUG_RENDER_VECTOR(State.Position,gs->Normal,GROUND_COLOR);
- VERBOSE_LOG((" not intersecting\r\n"));
- }
- } else {
-
- gs->OnGround = false;
- gs->SurfaceType = SURFACE_TYPE_DEFAULT;
- VERBOSE_LOG((" not on ground!\r\n"));
- }
- if (result.StartBad) {
- DEBUG_RENDER_AABOX(box,Vector3(1,0,0),0.25f);
- VERBOSE_LOG((" INTERSECTING!\r\n"));
- }
- }
- /***********************************************************************************************
- * Phys3Class::User_Move -- User Move mode. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::User_Move(float dt)
- {
- VERBOSE_LOG(("Phys3::User_Move\r\n"));
- if (Controller) {
-
- Vector3 move(Controller->Get_Move_Vector());
- move.Rotate_Z(Heading);
- State.Velocity = NormSpeed * move;
- move = State.Velocity * dt;
-
- return Apply_Move(move,dt);
- }
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Ballistic_Move -- Ballistic motion *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Ballistic_Move(float dt)
- {
- // Compute a move vector for the object flying through the air...
- WWPROFILE("Phys3::Ballistic_Move");
- VERBOSE_LOG(("Phys3::Ballistic_Move\r\n"));
- Vector3 move;
- float accel = PhysicsConstants::GravityAcceleration.Z * GravScale;
- Vector3 start_vel = State.Velocity;
- Vector3 start_pos = State.Position;
- move.X = State.Velocity.X * dt;
- move.Y = State.Velocity.Y * dt;
- move.Z = 0.5f * accel * dt * dt + State.Velocity.Z * dt;
- bool moved = Apply_Move(move,dt);
- // Compute X,Y velocities from the actual move that was performed
- State.Velocity.X = (State.Position.X - start_pos.X) / dt;
- State.Velocity.Y = (State.Position.Y - start_pos.Y) / dt;
- // Compute the analytical Z velocity and the ad-hoc Z velocity, the
- // more negative one is the one to keep. What this does is use the
- // analytical velocity unless the character hits a roof.
- // if (CollidedThisFrame) {
- // State.Velocity.Z = (State.Position.Z - start_pos.Z) / dt;
- // } else {
- State.Velocity.Z = start_vel.Z + accel * dt;
- // }
- return moved;
- }
- /***********************************************************************************************
- * Phys3Class::Slide_Move -- Sliding motion, down a slope *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Slide_Move(const GroundStateStruct & gs,float dt)
- {
- // Compute a move vector which causes the object to slide down the slope...
- WWPROFILE("Phys3::Slide_Move");
- VERBOSE_LOG(("Phys3::Slide_Move\r\n"));
- Vector3 start_pos = State.Position;
- Vector3 move = NormSpeed * dt * gs.Down;
- bool moved = Apply_Move(move,dt,true,false);
- State.Velocity = (State.Position - start_pos) / dt;
- return moved;
- }
- /***********************************************************************************************
- * Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite grip) *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Normal_Move(const GroundStateStruct & gs,float dt)
- {
- WWPROFILE("Phys3::Normal_Move");
- VERBOSE_LOG(("Phys3::Normal_Move\r\n"));
-
- // phys3 doesn't move unless on a slope or falling.
- State.Velocity.Set(0,0,0);
- return false;
- }
- /***********************************************************************************************
- * Phys3Class::Collide_Move -- Move in response to someone colliding with me *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Collide_Move(const Vector3 & requested_move,float dt)
- {
- WWPROFILE("Phys3::Collide_Move");
- VERBOSE_LOG(("Phys3::Collide_Move\r\n"));
- // if we're already being considered in a collision (collisions looped back to us) then return
- if (InCollision) {
- return false;
- }
-
- InCollision = true;
- MoveMode = COLLIDE_MOVE;
- Vector3 start_position = State.Position;
- bool moved = Apply_Move(requested_move,dt,true,false);
-
- /*
- ** if we moved and we started on the ground, try to stick to the ground
- */
- if (moved && OnGround) {
- Snap_To_Ground(State.Position - start_position,true);
- }
- InCollision = false;
- State.Velocity = (State.Position - start_position) / dt;
- Add_Animation_Move(State.Position - start_position);
-
- Update_Transform(true);
- Update_Cull_Box();
- return moved;
- }
- /***********************************************************************************************
- * Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Apply_Move
- (
- const Vector3 & input_move,
- float dt,
- bool allow_sliding,
- bool allow_stepping,
- bool stop_on_walkable
- )
- {
- WWPROFILE("Phys3::Apply_Move");
- VERBOSE_LOG(("Phys3::Apply_Move: %f %f %f\r\n",input_move.X,input_move.Y,input_move.Z));
-
- #ifdef WWDEBUG
- if (!input_move.Is_Valid()) {
- WWDEBUG_SAY(("Phys3::Apply_Move called with invalid move vector: %f, %f, %f\r\n",input_move.X,input_move.Y,input_move.Z));
- // WWASSERT(input_move.Is_Valid());
- return false;
- }
- #endif
- /*
- ** Early exit if we're not really moving!
- */
- float move_len = input_move.Length();
- if (move_len < WWMATH_EPSILON) {
- if (GroundState.OnGround) {
- Set_Flag(ASLEEP,true);
- }
- VERBOSE_LOG((" no motion\r\n"));
- return false;
- } else {
- VERBOSE_LOG((" applying move (%f,%f,%f)\r\n",input_move.X,input_move.Y,input_move.Z));
- Set_Flag(ASLEEP,false);
- Invalidate_Ground_State();
- }
- bool moved = false;
- bool done = false;
- bool collided = false;
- const int MAX_CONTACTS = 4;
- const int MAX_ITERATIONS = 4;
-
- int total_iterations = 0;
- int contact_count = 0;
- Vector3 contacts[MAX_CONTACTS];
-
- Vector3 start_position = State.Position;
- Vector3 move = input_move;
- Vector3 move_dir = move / move_len;
- bool already_stepped = false;
- Inc_Ignore_Counter();
-
- /*
- ** Apply Move:
- **
- ** - WHILE (I still have movement to apply)
- ** - sweep our collision box up to the first collision
- ** - IF (I started out intersecting) give up, done
- ** - ELSE
- ** - IF (I was able to take the entire move without collision) accept the move, done
- ** - ELSE
- ** - Accept the portion of the move up to the collision
- ** - Call the collision callbacks
- ** - IF (I have no movement remaining OR I'm not allowed to slide) done
- ** - ELSE
- ** - If (I can push the obstacle out of my way) push obstacle, go back to top of loop
- ** - If (I can shatter this object) shatter it, go back to top of loop
- ** - If (This contact is a slope, and I'm trying to walk up it) modify contact to act like a wall
- ** - Reflect the movement vector so that it does not violate the current contacts
- **
- ** Notes:
- ** - Try to stop movement some distance (COLLISION_EPSILON) away from collisions.
- ** - When approaching slides (slopes that we'll slide off of), try to treat them like walls.
- ** - Can't always treat slides like walls since we can fall onto them or walk onto them from the top...
- ** - Trying to get stepping for free usually by just always bumping the character up before taking his move
- **
- */
-
- while (!done) {
-
- if ( ++total_iterations > MAX_ITERATIONS ) {
- VERBOSE_LOG(( "Phys3 Exceeded max iterations!\r\n" ));
- done = true;
- }
- /*
- ** Get the collision box for the object
- */
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- CastResultStruct result;
- PhysAABoxCollisionTestClass test( box,
- move,
- &result,
- Get_Collision_Group(),
- COLLISION_TYPE_PHYSICAL );
-
- /*
- ** If we're allowed to step up, try to
- */
- if (OnGround && allow_stepping && !already_stepped) {
- already_stepped = true;
- /*
- ** Bump the box up Z by STEP_HEIGHT
- */
- test.Translate(Vector3(0,0,STEP_HEIGHT));
- /*
- ** Sweep the box
- */
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
-
- /*
- ** IF: the 'step' test did not start out intersecting (no low roof) then
- ** warp our position up and continue
- **
- ** ELSE: Since the step test started out intersecting we must be under a low
- ** roof. In this case, perform the move from our original position; no
- ** stepping over obstacles.
- */
- if (!result.StartBad) {
-
- State.Position.Z += STEP_HEIGHT;
- moved = true;
- } else {
- result.Reset();
- test.Translate(Vector3(0,0,-STEP_HEIGHT));
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
-
- }
-
- } else {
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
- }
-
- VERBOSE_LOG((" Checking collision results: "));
- if (result.StartBad) {
- //WWDEBUG_WARNING(("result.StartBad\n"));
- //PhysicsSceneClass::Get_Instance()->Add_Debug_AABox(box,Vector3(1,0,0));
- VERBOSE_LOG(("StartBad!\r\n"));
- done = true;
-
- } else {
- if (result.Fraction == 1.0f) {
- /*
- ** the move was successful, accept it.
- */
- VERBOSE_LOG(("Entire move accepted.\r\n"));
- State.Position += move;
-
- done = true;
- moved = true;
- } else {
- VERBOSE_LOG(("Collided with %s. normal=(%f,%f,%f)\r\n",test.CollidedRenderObj->Get_Name(),result.Normal.X,result.Normal.Y,result.Normal.Z));
- collided = true;
- /*
- ** How far should we stay away from this obstacle
- */
- float epsilon = GROUND_EPSILON;
- if (result.Normal.Z < SlideNormalZ) { // TODO: base this on surface type?
- epsilon = WALL_EPSILON;
- }
- /*
- ** Try to move up to the obstacle
- */
- float move_len = result.Fraction * move.Length();
- if (move_len > epsilon) {
- move_len -= epsilon;
- Vector3 direction = move;
- direction.Normalize();
- State.Position += move_len * direction;
- move -= result.Fraction * move;
- dt -= result.Fraction * dt;
- moved = true;
- contact_count = 0; // since we moved, reset the contacts
- }
-
- /*
- ** If there is no time remaining, no movement remaining,
- ** or we're not allowed to "slide" along contacts then we're done
- */
- if ( (dt < WWMATH_EPSILON) ||
- (move.Length2() < WWMATH_EPSILON) ||
- (allow_sliding == false) )
- {
-
- /*
- ** we're close enough to done
- */
- done = true;
-
- } else {
-
- /*
- ** We hit something, call the collision callbacks.
- */
- WWASSERT(test.CollidedPhysObj != NULL);
- CollisionReactionType reaction = COLLISION_REACTION_DEFAULT;
- CollisionEventClass event;
- event.OtherObj = test.CollidedPhysObj;
- reaction |= Collision_Occurred(event);
- event.OtherObj = this;
- reaction |= test.CollidedPhysObj->Collision_Occurred(event);
-
- if (reaction & COLLISION_REACTION_NO_BOUNCE) {
-
- /*
- ** If we're instructed to not bounce, just erase the remaining time
- */
- dt = 0.0f;
-
- } else {
- /*
- ** assume we're going to have to clip the move
- */
- bool clip_move = true;
-
- /*
- ** PUSH: Try to push the obstacle
- */
- if (clip_move) {
- Phys3Class * p3obj = test.CollidedPhysObj->As_Phys3Class();
- if (p3obj != NULL) {
- /*
- ** Compute a move for the object we're contacting which makes it slide out
- ** of our way if we're moving at an angle to it.
- */
- Vector3 dc;
- Vector3::Subtract(p3obj->Get_Position(),State.Position,&dc);
- dc.Normalize();
- float fraction = 1.0f;
- if ((Mass > WWMATH_EPSILON) && (p3obj->Get_Mass() > WWMATH_EPSILON)) {
- fraction = (Mass / (Mass + p3obj->Get_Mass()));
- }
- dc *= fraction * move.Length();
- Dec_Ignore_Counter();
- if (p3obj->Collide_Move(dc,dt)) {
- clip_move = false;
- }
- Inc_Ignore_Counter();
- }
- }
-
- /*
- ** SHATTER: If the mesh is shatterable, shatter it. We are doing this so that
- ** shatterable meshes are purely cosmetic for multiplay.
- */
- MeshClass * mesh = NULL;
- if ((test.CollidedRenderObj != NULL) && (test.CollidedRenderObj->Class_ID() == RenderObjClass::CLASSID_MESH)) {
- mesh = (MeshClass*)test.CollidedRenderObj;
- }
- if ( (mesh != NULL) &&
- (mesh->Get_W3D_Flags() & W3D_MESH_FLAG_SHATTERABLE) &&
- (mesh->Is_Not_Hidden_At_All()))
- {
- PhysicsSceneClass::Get_Instance()->Shatter_Mesh( mesh,
- State.Position,
- result.Normal,
- State.Velocity );
- if (Observer != NULL) {
- Observer->Object_Shattered_Something(this,test.CollidedPhysObj,result.SurfaceType);
- }
- clip_move = false;
- }
- /*
- ** CLIP: If the obstacle didn't move out of our way and we didn't step over it,
- ** add the new contact and adjust our move to not hit it again
- */
- if (clip_move) {
- if (contact_count < MAX_CONTACTS) {
-
- Vector3 normal = result.Normal;
- if ( (normal.Z < SlideNormalZ) &&
- (normal.Z > 0.0f) &&
- (GroundState.OnGround) &&
- (GroundState.Normal.Z > SlideNormalZ) &&
- (MoveMode == BALLISTIC_MOVE))
- {
- normal.Z = 0.0f;
- normal.Normalize();
- }
- contacts[contact_count++] = normal;
- DEBUG_RENDER_VECTOR(State.Position,1.5*normal,CONTACT_COLOR);
- } else {
- VERBOSE_LOG(("exceeded max contacts in Phys3::Apply_Move! position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
- done = true;
- }
- Clip_Move(contacts,contact_count,&move);
- }
- }
- }
- }
- }
- }
- Dec_Ignore_Counter();
- return moved;
- }
- /***********************************************************************************************
- * Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- inline bool Phys3Class::Debug_Verify_Position(void)
- {
- #ifdef VERBOSE_LOGGING
- PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- NonRefPhysListClass list;
- PhysAABoxIntersectionTestClass test(box,Get_Collision_Group(),COLLISION_TYPE_PHYSICAL,&list);
- Inc_Ignore_Counter();
- bool intersecting = scene->Intersection_Test(test);
- Dec_Ignore_Counter();
- if (intersecting) {
- VERBOSE_LOG((" %s is intersecting %s!\r\n",Model->Get_Name(),list.Peek_Head()->Peek_Model()->Get_Name()));
- } else {
- VERBOSE_LOG((" %s not intersecting\r\n",Model->Get_Name()));
- }
- return intersecting;
- #else
- return true;
- #endif
- }
- /***********************************************************************************************
- * Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Snap_To_Ground(const Vector3 & actual_move,bool was_stepping)
- {
- if (Get_Flag(ASLEEP)) {
- return;
- }
-
- VERBOSE_LOG(("Phys3::Snap_To_Ground\r\n"));
- /*
- ** Call this if you started a timestep on the ground and want to stay on the ground...
- ** If we were on the ground, we snap down to the ground to ensure that the object does not
- ** fly unrealistically off edges, etc. The logic works like this:
- ** - the object may have to snap at least back down to the same altitude it started at
- ** - in addition to this, we want to keep the object "stuck" on slopes up to SlideAngle
- ** - so, cast its box down this much: delta_z + delta_xy * tan(SlideAngle)
- ** - if what we hit has a slope that less than SlideAngle, snap down!
- */
- float delta_z = actual_move.Z;
- float delta_xy = WWMath::Sqrt(actual_move.X * actual_move.X + actual_move.Y * actual_move.Y);
- /*
- ** If we don't need to snap down, just return
- */
- if (delta_z + delta_xy > 0.0f) {
-
- float snap_dist = delta_z + delta_xy * SlideAngleTan + GROUND_DISTANCE;
-
- Inc_Ignore_Counter();
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- CastResultStruct result;
- PhysAABoxCollisionTestClass test(box,Vector3(0,0,-snap_dist),&result,Get_Collision_Group());
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
- if ((result.Fraction > 0.0f) && (result.Fraction <= 1.0f) && (!result.StartBad)) {
-
- float snap_down = 0;
- if (result.Normal.Z > SlideNormalZ) {
- /*
- ** Case 1: ground is within snap distance and that ground is a walkable slope
- ** Snap down to within GROUND_EPSILON of the ground.
- */
- snap_down = snap_dist * result.Fraction;
- if (snap_down > GROUND_EPSILON) {
- State.Position.Z -= snap_down - GROUND_EPSILON;
- }
- OnGround = true;
- Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);
- DEBUG_RENDER_VECTOR(State.Position,result.Normal,GROUND_COLOR);
- VERBOSE_LOG((" now above walkable slope, snapped down %f\r\n",snap_down - GROUND_EPSILON));
- } else {
- /*
- ** Case 2: ground is within snap distance but it is not a walkable slope
- ** If we were stepping, we should snap down STEP_HEIGHT if we can.
- */
- OnGround = false;
- if (was_stepping) {
- // (gth) just let the character snap down to slopes too!
- //snap_down = WWMath::Min(STEP_HEIGHT,snap_dist * result.Fraction);
- if (snap_down > GROUND_EPSILON) {
- State.Position.Z -= snap_down - GROUND_EPSILON;
- }
- }
-
- Link_To_Carrier(NULL);
- VERBOSE_LOG((" now above non-walkable slope, snapped only up to step-height %f\r\n",snap_down-GROUND_EPSILON));
- }
-
- } else {
-
- /*
- ** Case 3: ground is not within snap distance.
- ** If we were stepping, snap back down the step distance.
- */
- OnGround = false;
- if (was_stepping) {
- State.Position.Z -= STEP_HEIGHT;
- }
- VERBOSE_LOG((" now airborne\r\n"));
- }
- /*
- ** Update the GroundState
- */
- GroundState.Init_From_Collision_Result(test,GROUND_EPSILON);
- Dec_Ignore_Counter();
- }
- }
- /***********************************************************************************************
- * Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move)
- {
- WWASSERT(contacts != NULL);
- VERBOSE_LOG(("Phys3::Clip_Move\r\n"));
- #ifdef WWDEBUG
- float move_len2 = move->Length2();
- #endif
-
- // Loop over all of the contacts.
- // On each one we modify our move vector to parallel that plane.
- // Immediately after each modification, we check if the other planes are a problem.
- // At the first time all planes are satisfied, we're done
- for (int i=0; i<contact_count; i++) {
-
- #ifdef WWDEBUG
- if (fabs(contacts[i].Length() - 1.0f) > WWMATH_EPSILON) {
- WWDEBUG_SAY(("Bad Contact Normal: %f %f %f Length = %f\r\n",contacts[i].X,contacts[i].Y,contacts[i].Z,contacts[i].Length()));
- }
- #endif
- if (!( fabs(contacts[i].Length() - 1.0f) < WWMATH_EPSILON )) {
- *move = Vector3(0,0,0);
- return;
- }
- VERBOSE_LOG((" normal[%d] = %f %f %f\r\n",i,contacts[i].X,contacts[i].Y,contacts[i].Z));
-
- // push the velocity a little bit away from the plane
- float dot = Vector3::Dot_Product(*move,contacts[i]);
- if (dot < 0.0f) {
- Vector3 adjustment = 1.01f * dot * contacts[i];
- *move -= adjustment;
- //WWASSERT(Vector3::Dot_Product(*move,contacts[i]) >= 0.0f);
- }
-
- for (int j=0; j<contact_count; j++) {
- float check = Vector3::Dot_Product(*move,contacts[j]);
- if (check < 0.0f) {
- break; // this contact isn't happy yet... keep choppin.
- }
- }
- if (j == contact_count) {
- break; // all contacts are happy!
- }
- }
- #ifdef WWDEBUG
- float new_move_len2 = move->Length2();
- WWASSERT(new_move_len2 <= move_len2 + WWMATH_EPSILON);
- #endif
- }
- /***********************************************************************************************
- * Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
- {
- WWASSERT(set_obj_space_box != NULL);
- *set_obj_space_box = CollisionBox;
- }
- /***********************************************************************************************
- * Phys3Class::Get_Collision_Box -- returns the current world space collision box *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 1/4/2001 gth : Created. *
- *=============================================================================================*/
- void Phys3Class::Get_Collision_Box(AABoxClass * set_box)
- {
- WWASSERT(set_box != NULL);
- Compute_WS_Collision_Box(State,set_box);
- }
- /***********************************************************************************************
- * Phys3Class::Push -- Move in response to an animated object colliding with me *
- * *
- * This function is called when StaticAnimPhys objects detect a collision. The key is that *
- * we don't want to slide along any contacts; just move as far as we can along the given *
- * move vector. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Push(const Vector3 & move)
- {
- Vector3 old_pos = State.Position;
- Apply_Move(move,1.0f,false,false);
- Update_Transform(true);
- Update_Cull_Box();
- Vector3 error = (State.Position - old_pos) - move;
- #if VERBOSE_LOGGING
- Vector3 actual_move = State.Position - old_pos;
- bool success = (State.Position - old_pos) == move;
- VERBOSE_LOG((" Phys3::Push. Apparent success: %d Error: %f %f %f\r\n",success, error.X,error.Y,error.Z));
- #endif
- return (error.Length2() < WWMATH_EPSILON * WWMATH_EPSILON);
- }
- /***********************************************************************************************
- * Phys3Class::Collide -- Move in response to a collision *
- * *
- * This function is similar to the 'Push' function, however the phys3 object is allowed to *
- * slide along geometry in order to get out of the way. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 1/4/2001 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Collide(const Vector3 & move)
- {
- Vector3 old_pos = State.Position;
- Collide_Move(move,1.0f);
- return ((State.Position - old_pos) == move);
- }
- /***********************************************************************************************
- * Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Can_Teleport(const Matrix3D &test_tm, bool check_dyn_only, NonRefPhysListClass * result_list)
- {
- //
- // Determine what the world-space collision box for
- // this object would be at the given transform.
- //
- StateStruct test_state;
- test_state.Position.Set (test_tm.Get_Translation ());
- test_state.Velocity.Set (0, 0, 0);
-
- AABoxClass collision_box;
- Compute_WS_Collision_Box (test_state, &collision_box);
- //
- // Intersect the box with the world.
- //
- PhysAABoxIntersectionTestClass test(collision_box,
- Get_Collision_Group(),
- COLLISION_TYPE_PHYSICAL,
- result_list);
- if (check_dyn_only) {
- test.CheckStaticObjs = false;
- }
-
- Inc_Ignore_Counter ();
- bool intersect = PhysicsSceneClass::Get_Instance ()->Intersection_Test(test);
- Dec_Ignore_Counter ();
- return (intersect == false);
- }
- /***********************************************************************************************
- * Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos *
- * *
- * Also returns an alternate position if the object cannot occupy the specified position. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Can_Teleport_And_Stand(const Matrix3D &test_tm, Matrix3D *new_tm)
- {
- //
- // Determine what the world-space collision box for
- // this object would be at the given transform.
- //
- StateStruct test_state;
- test_state.Position.Set (test_tm.Get_Translation ());
- test_state.Velocity.Set (0, 0, 0);
-
- AABoxClass collision_box;
- Compute_WS_Collision_Box (test_state, &collision_box);
- Inc_Ignore_Counter ();
- //
- // Cast a box 'down' from this location.
- // This checks to make sure it doesn't intersect anything at its
- // starting position, and its not off the edge of a cliff
- //
- CastResultStruct result;
- Vector3 move_vector (0, 0, -5.0F);
- PhysAABoxCollisionTestClass test( collision_box,
- move_vector,
- &result,
- 0,
- COLLISION_TYPE_PHYSICAL);
- PhysicsSceneClass::Get_Instance ()->Cast_AABox (test);
- Dec_Ignore_Counter ();
- //
- // Did the box land on a suface we can stand on?
- //
- bool retval = false;
- if ((result.StartBad == false) && ((result.Fraction == 1.0F) || (result.Normal.Z >= SlideNormalZ))) {
-
- //
- // Calculate a new transform for the object
- //
- Vector3 new_pos = test_tm.Get_Translation () + (result.Fraction * move_vector);
- (*new_tm) = test_tm;
- new_tm->Set_Translation (new_pos);
- retval = true;
- }
-
- return retval;
- }
- /***********************************************************************************************
- * Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 9/16/2000 gth : Created. *
- *=============================================================================================*/
- bool Phys3Class::Find_Teleport_Location
- (
- const Vector3 & start,
- float radius,
- Vector3 * out
- )
- {
- bool retval = false;
- //
- // Test the actual point the caller passed to us.
- //
- Matrix3D initial_tm (start);
- if (Can_Teleport_And_Stand (initial_tm, &initial_tm)) {
- (*out) = initial_tm.Get_Translation ();
- return true;
- }
- const int MAX_ATTEMPTS = 10;
- const float MIN_DIST = 0.1F;
-
- //
- // Try a number of times to find a valid location
- //
- for (int index = 0; !retval && index < MAX_ATTEMPTS; index ++) {
-
- //
- // Get a random distance and direction to make the attempt in
- //
- float dist = WWMath::Random_Float (MIN_DIST, radius);
- float angle = WWMath::Random_Float (0, DEG_TO_RADF (360));
- float z_delta = dist * SlideNormalZ;
-
- //
- // Calculate what the new transform would be at this location
- //
- Vector3 test_pos;
- test_pos.X = start.X + WWMath::Cos (angle) * dist;
- test_pos.Y = start.Y + WWMath::Sin (angle) * dist;
- test_pos.Z = start.Z + z_delta;
- Matrix3D test_tm (test_pos);
- //
- // Do the test to make sure this is a valid teleport location.
- //
- Matrix3D new_tm (1);
- if (Can_Teleport_And_Stand (test_tm, &new_tm)) {
- (*out) = new_tm.Get_Translation ();
- retval = true;
- }
- }
- return retval;
- }
- bool Phys3Class::Can_Move_To(const Matrix3D &new_tm)
- {
- //
- // Do a sweep from our current position to the new position.
- //
- Vector3 start_position = Get_Position();
- Vector3 new_position = new_tm.Get_Translation();
- Vector3 move = new_position - start_position;
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- Inc_Ignore_Counter();
- CastResultStruct result;
- PhysAABoxCollisionTestClass test( box,
- move,
- &result,
- 0,
- COLLISION_TYPE_PHYSICAL );
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
- Dec_Ignore_Counter();
- return (result.Fraction == 1.0f);
- }
- void Phys3Class::Assert_State_Valid(void)
- {
- AABoxClass box;
- Compute_WS_Collision_Box(State,&box);
- CastResultStruct result;
- PhysAABoxCollisionTestClass test( box,
- Vector3(0,0,0),
- &result,
- 0,
- COLLISION_TYPE_PHYSICAL );
- Inc_Ignore_Counter();
- PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
- Dec_Ignore_Counter();
-
- #if VERBOSE_LOGGING
- if (result.StartBad) {
- VERBOSE_LOG(("Phys3 Intersection! ModelName = %s\r\n",Model->Get_Name()));
- }
- #endif
- WWASSERT(State.Position.Is_Valid());
- WWASSERT(State.Velocity.Is_Valid());
- }
- void Phys3Class::Network_State_Update(const Vector3 & pos,const Vector3 & vel)
- {
- Vector3 delta = pos - State.Position;
- Set_Position(pos);
- Set_Velocity(vel);
- Snap_To_Ground(delta,false);
- Update_Transform( true );
- }
- void Phys3Class::Network_Latency_State_Update(const Vector3 & net_pos,const Vector3 & net_vel)
- {
- DEBUG_RENDER_AABOX(AABoxClass(CollisionBox.Center + net_pos,CollisionBox.Extent),Vector3(0,1,0),0.25f);
- /*
- ** Allocate a history object if needed
- */
- if (History == NULL) {
- History = new Phys3HistoryClass;
- History->Init(net_pos,net_vel);
- }
- WWASSERT(History != NULL);
- /*
- ** Search our history to find the point nearest this server update
- */
- Vector3 old_pos;
- History->Find_Nearest_Point(net_pos,net_vel,&old_pos);
- LatencyError = net_pos - old_pos;
- LastKnownPosition = net_pos;
- LastKnownVelocity = net_vel;
- }
- void Phys3Class::Network_Teleport_Correction(void)
- {
- Vector3 new_pos = LastKnownPosition;
- Vector3 correction = new_pos - State.Position;
- Set_Position(new_pos);
- Set_Velocity(LastKnownVelocity);
- #if 0
- History->Apply_Correction(correction);
- #else
- History->Init(LastKnownPosition,LastKnownVelocity);
- #endif
- LatencyError.Set(0,0,0);
- }
- /*************************************************************************************
- **
- ** Phys3Class Save-Load support
- **
- *************************************************************************************/
- const PersistFactoryClass & Phys3Class::Get_Factory (void) const
- {
- return _Phys3Factory;
- }
- bool Phys3Class::Save (ChunkSaveClass &csave)
- {
- csave.Begin_Chunk(PHYS3_CHUNK_MOVEABLEPHYS);
- MoveablePhysClass::Save(csave);
- csave.End_Chunk();
- csave.Begin_Chunk(PHYS3_CHUNK_VARIABLES);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_ONGROUND,OnGround);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_INCOLLISION,InCollision);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_HEADING,Heading);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_MOVEMODE,MoveMode);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_POSITION,State.Position);
- WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_VELOCITY,State.Velocity);
- csave.End_Chunk();
- return true;
- }
- bool Phys3Class::Load (ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk()) {
-
- switch(cload.Cur_Chunk_ID())
- {
- case PHYS3_CHUNK_MOVEABLEPHYS:
- MoveablePhysClass::Load(cload);
- break;
- case PHYS3_CHUNK_VARIABLES:
- while (cload.Open_Micro_Chunk()) {
- switch(cload.Cur_Micro_Chunk_ID()) {
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_ONGROUND,OnGround);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_INCOLLISION,InCollision);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_HEADING,Heading);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_MOVEMODE,MoveMode);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_POSITION,State.Position);
- READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_VELOCITY,State.Velocity);
- }
- cload.Close_Micro_Chunk();
- }
- break;
- default:
- WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
- break;
- }
-
- cload.Close_Chunk();
- }
- Invalidate_Ground_State();
- SaveLoadSystemClass::Register_Post_Load_Callback(this);
- return true;
- }
- void Phys3Class::On_Post_Load (void)
- {
- MoveablePhysClass::On_Post_Load();
- Update_Cached_Model_Parameters();
- }
- /*************************************************************************************
- **
- ** Phys3Class::GroundStateStruct Implementation
- **
- *************************************************************************************/
- Phys3Class::GroundStateStruct::GroundStateStruct(void) :
- IsDirty(true),
- OnGround(false),
- OnDynamicObj(false),
- SurfaceType(SURFACE_TYPE_DEFAULT),
- Height(0.0f),
- Normal(0,0,1),
- Down(1,0,0),
- GroundObject(NULL),
- GroundRenderObject(NULL)
- {
- Reset();
- }
- void Phys3Class::GroundStateStruct::Reset(void)
- {
- IsDirty = true;
- OnGround = false;
- OnDynamicObj = false;
- SurfaceType = SURFACE_TYPE_DEFAULT;
- Height = 0.0f;
- Normal.Set(0,0,1);
- Down.Set(1,0,0);
- GroundObject = NULL;
- GroundRenderObject = NULL;
- }
- void Phys3Class::GroundStateStruct::Init_From_Collision_Result(PhysAABoxCollisionTestClass & test,float height)
- {
- Reset();
- IsDirty = false;
- if (test.Result->Fraction < 1.0f) {
- OnGround = true;
- SurfaceType = test.Result->SurfaceType;
- Normal = test.Result->Normal;
- GroundObject = test.CollidedPhysObj;
- GroundRenderObject = test.CollidedRenderObj;
- Height = height;
- if (GroundObject != NULL) {
- if ( (GroundObject->As_HumanPhysClass() != NULL) ||
- (GroundObject->As_RigidBodyClass() != NULL))
- {
- OnDynamicObj = true;
- }
- }
- if (test.Result->StartBad) {
-
- IsDirty = true;
- Normal.Set(0,0,1);
- // VERBOSE_LOG(("ERROR - intersecting object: %s!\r\n",GroundRenderObject->Get_Name()));
- } else {
- if (Normal.Length2() <= 0.0f) {
- WWDEBUG_SAY(("ERROR - detected non-unit normal!\r\n"));
- }
-
- // compute the down vector for this plane
- // down = N x -Z x N
- Vector3 tmp;
- Vector3::Cross_Product(Normal,Vector3(0,0,-1),&tmp);
- Vector3::Cross_Product(tmp,Normal,&(Down));
- Down.Normalize();
-
- // 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)));
- }
- } else {
- OnGround = false;
- OnDynamicObj = false;
- GroundObject = NULL;
- GroundRenderObject = NULL;
- SurfaceType = SURFACE_TYPE_DEFAULT;
- }
- }
- /***********************************************************************************************
- **
- ** Phys3DefClass Implementation
- **
- ***********************************************************************************************/
- /*
- ** Declare a PersistFactory for Phys3DefClasses
- */
- SimplePersistFactoryClass<Phys3DefClass,PHYSICS_CHUNKID_PHYS3DEF> _Phys3DefFactory;
- /*
- ** Chunk ID's used by MoveablePhysDefClass
- */
- enum
- {
- PHYS3DEF_CHUNK_MOVEABLEPHYSDEF = 0x04486000, // (parent class)
- PHYS3DEF_CHUNK_VARIABLES,
- PHYS3DEF_VARIABLE_NORMSPEED = 0x00,
- PHYS3DEF_VARIABLE_SLIDEANGLE,
- PHYS3DEF_VARIABLE_STEPHEIGHT,
- };
- Phys3DefClass::Phys3DefClass(void) :
- NormSpeed(DEFAULT_NORMALIZED_SPEED),
- SlideAngle(DEFAULT_SLIDE_ANGLE),
- StepHeight(DEFAULT_STEP_HEIGHT)
- {
- // make our parameters editable!
- EDITABLE_PARAM(Phys3DefClass, ParameterClass::TYPE_FLOAT, NormSpeed);
- ANGLE_EDITABLE_PARAM(Phys3DefClass, SlideAngle, DEG_TO_RADF(0.0f), DEG_TO_RADF(90.0f));
- FLOAT_EDITABLE_PARAM(Phys3DefClass, StepHeight, 0.0f, 10.0f);
- }
- const PersistFactoryClass & Phys3DefClass::Get_Factory (void) const
- {
- return _Phys3DefFactory;
- }
- uint32 Phys3DefClass::Get_Class_ID (void) const
- {
- return CLASSID_PHYS3DEF;
- }
- PersistClass * Phys3DefClass::Create(void) const
- {
- Phys3Class * obj = NEW_REF(Phys3Class,());
- obj->Init(*this);
- return obj;
- }
- bool Phys3DefClass::Save(ChunkSaveClass &csave)
- {
- csave.Begin_Chunk(PHYS3DEF_CHUNK_MOVEABLEPHYSDEF);
- MoveablePhysDefClass::Save(csave);
- csave.End_Chunk();
- csave.Begin_Chunk(PHYS3DEF_CHUNK_VARIABLES);
- WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
- WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
- WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
- csave.End_Chunk();
- return true;
- }
- bool Phys3DefClass::Load(ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk()) {
- switch(cload.Cur_Chunk_ID()) {
- case PHYS3DEF_CHUNK_MOVEABLEPHYSDEF:
- MoveablePhysDefClass::Load(cload);
- break;
- case PHYS3DEF_CHUNK_VARIABLES:
- while (cload.Open_Micro_Chunk()) {
- switch(cload.Cur_Micro_Chunk_ID()) {
- READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
- READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
- READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
- }
- cload.Close_Micro_Chunk();
- }
- break;
- default:
- WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
- break;
- }
- cload.Close_Chunk();
- }
- return true;
- }
- bool Phys3DefClass::Is_Type(const char * type_name)
- {
- if (stricmp(type_name,Phys3DefClass::Get_Type_Name()) == 0) {
- return true;
- } else {
- return MoveablePhysDefClass::Is_Type(type_name);
- }
- }
|