123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953 |
- /*
- ** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
- **
- ** File: HitTest.cpp
- **
- ** Author:
- **
- ** Description:
- **
- ** History:
- */
- #include "pch.h"
- void HitTest::SetBB(Time start,
- Time stop,
- float dt)
- {
- assert (stop >= start);
- assert (m_timeStop >= m_timeStart);
- if (m_staticF)
- {
- m_tStart = 0.0f;
- m_tStop = dt;
- m_lastTest = NULL;
- }
- else
- {
- if (m_timeStart == m_timeStop)
- {
- //hittest always exists (or, at least, started before
- //start and ends after stop)
- m_tStart = 0.0f;
- m_tStop = dt;
- m_stopPosition = GetPosition() + m_velocity * m_tStop;
- }
- else if (stop < m_timeStart)
- {
- //The object shouldn't exist yet ... flag it as dead
- m_deadF = true;
- m_stopPosition = GetPosition();
- }
- else if (m_timeStop > start)
- {
- if (start < m_timeStart)
- {
- //The object does not come into existance until after start.
- m_tStart = m_timeStart - start;
- //Back the object up so that it is in the correct position at time 0.
- SetPosition(GetPosition() - m_velocity * m_tStart);
- m_deadF = false; //just in case it was disabled by the above test
- }
- else
- {
- m_tStart = 0.0f;
- assert (!m_deadF);
- }
- m_tStop = ((m_timeStop < stop) ? m_timeStop : stop) - start;
- m_stopPosition = GetPosition() + m_velocity * m_tStop;
- }
- else
- {
- //The will die before the time interval of interest starts
- m_deadF = true;
- m_stopPosition = GetPosition();
- }
- assert (m_stopPosition.LengthSquared() >= 0.0f);
- assert (m_stopPosition.LengthSquared() < 1.0e12f);
- UpdateBB();
- }
- }
- void HitTest::UpdateBB(void)
- {
- if (!m_staticF)
- {
- Vector startPosition = m_tStart == 0.0f
- ? GetPosition()
- : GetPosition() + m_velocity * m_tStart; //offset for delayed start
- //let's assume were using the xy & z axes.
- assert (c_nAxes == 3);
- for (int i = 0; (i < c_nAxes); i++)
- {
- float v1 = startPosition[i];
- float v2 = m_stopPosition[i];
-
- if (v1 <= v2)
- {
- m_boundingBox.axes[i].values[0] = v1 - m_radius;
- m_boundingBox.axes[i].values[1] = v2 + m_radius;
- }
- else
- {
- m_boundingBox.axes[i].values[0] = v2 - m_radius;
- m_boundingBox.axes[i].values[1] = v1 + m_radius;
- }
- m_endpoints[i][0].value = m_boundingBox.axes[i].values[0];
- m_endpoints[i][1].value = m_boundingBox.axes[i].values[1];
- }
- }
- m_lastTest = NULL;
- }
- const double epsilon = 1.0f / 128.0f;
- void HitTest::Collide(HitTest* pHitTest,
- CollisionQueue* pQueue)
- {
- assert (pQueue);
- const BoundingBox& hitTestBB = pHitTest->m_boundingBox;
- if ((m_boundingBox.axes[0].values[1] > hitTestBB.axes[0].values[0]) &&
- (m_boundingBox.axes[0].values[0] < hitTestBB.axes[0].values[1]) &&
- (m_boundingBox.axes[1].values[1] > hitTestBB.axes[1].values[0]) &&
- (m_boundingBox.axes[1].values[0] < hitTestBB.axes[1].values[1]) &&
- (m_boundingBox.axes[2].values[1] > hitTestBB.axes[2].values[0]) &&
- (m_boundingBox.axes[2].values[0] < hitTestBB.axes[2].values[1]))
- {
- //Calculate the time when pHitTest and this first collide (if ever)
- Vector dP = GetPosition() - pHitTest->GetPosition();
- float r = pHitTest->m_radius + m_radius;
- double c = (double)(dP * dP) - (double)(r * r);
- Vector dV = pHitTest->m_velocity - m_velocity; //negative to eliminate an extraneous - below
- //Distance(t) = |dP - dV * t|
- //Distance(t)^2 = (dP - dV * t) * (dP - dV * t)
- // = (dP * dP - 2 * dP * dV * t + dV * dV * t^2)
- //Solve for Distance(t)^2 == radius^2 : a*t^2 - b * t + c = 0 => t = (b +- sqrt(b^2 - 4ac)) / 2a
- //
- bool fCollision = false;
- float tCollision;
- float tMax;
- double halfB = (dP * dV); //b/2
- if (halfB > 0.0) //objects need to be closing at least a little to generate a collision
- {
- double a = dV * dV;
- if (a > 0.0) //Shouldn't be a problem but ... float do have their limits of resolution
- {
- double b2ac = halfB*halfB - a * c;
- if (b2ac >= 0.0)
- {
- //real roots ... at some point (past or future), they'll be close enough to collide
- //pick the minimum time: when they would first collide
- double s = sqrt(b2ac);
- tCollision = (c <= 0.0) ? 0.0f //Bounding spheres already intersect
- : (float)((halfB - s) / a);
- tMax = (float)((halfB + s) / a);
- if (tMax > m_tStop)
- tMax = m_tStop;
- fCollision = true;
- }
- }
- }
- else if (c < 0.0)
- {
- //The object bounding sphere's overlap ... we have a possible collision even if the
- //objects are not moving (or are moving apart)
- tCollision = m_tStart;
- tMax = FLT_MAX;
- if (halfB != 0.0)
- {
- //The objects are moving apart ... when will their bounding spheres no longer overlap.
- double a = dV * dV;
- if (a > 0.0) //Shouldn't be a problem but ... float do have their limits of resolution
- {
- tMax = (float)((halfB + (float)sqrt(halfB*halfB - a * c)) / a);
- }
- }
- if (tMax > m_tStop)
- tMax = m_tStop;
- fCollision = true;
- }
- //Is it in the window of time we are interested in?
- //The range of legal times is always updated in updateBB() and
- //projectiles will always have the most restrictive range (and
- //in a projectile vs. non-projectile, the projectile is always this).
- //
- //Allow collisions that occur slightly beyond m_tStop to guarantee that
- //all collisions get processed at least once (this could lead to collisions
- //happening twice but the less of a problem than missing a collision)
- if (fCollision && (tCollision >= m_tStart) && (tCollision <= m_tStop + (float)epsilon))
- {
- HitTestShape htsThis = m_shape;
- HitTestShape htsPHT = pHitTest->m_shape;
- if (((m_shape <= c_htsSphere) && (pHitTest->m_shape <= c_htsSphere)) ||
- HullCollide(&tCollision, tMax + (float)epsilon, this, &htsThis, pHitTest, &htsPHT, dP, dV))
- {
- pQueue->addCollision(tCollision, this, htsThis, pHitTest, htsPHT);
- }
- }
- }
- }
- class BoundingPoint : public HitTest
- {
- public:
- BoundingPoint(IObject* d, bool staticF)
- :
- HitTest(d, 0.0f, staticF, c_htsPoint)
- {
- }
- };
- class BoundingSphere : public HitTest
- {
- public:
- BoundingSphere(IObject* d, bool staticF)
- :
- HitTest(d, 0.0f, staticF, c_htsSphere)
- {
- }
- };
- class ConvexVertex
- {
- public:
- const Vector& GetPosition(void) const
- {
- return m_position;
- }
- void SetPosition(const Vector& p)
- {
- m_position = p;
- }
- const ConvexVertex** GetAdjacencies(void) const
- {
- return m_adjacencies;
- }
- private:
- Vector m_position;
- const ConvexVertex** m_adjacencies;
- friend class HitTest;
- friend class BoundingHull;
- friend class SingleHull;
- };
- class BoundingCone : public HitTest
- {
- public:
- BoundingCone(IObject* d, bool staticF)
- :
- HitTest(d, 0.0f, staticF, c_htsCone)
- {
- }
- Vector GetExtreme(const Vector& direction)
- {
- //On the cone portion of the cone
- if (direction.z >= (sqrt2 / 2.0f))
- {
- return Vector(0.0f, 0.0f, GetRadius()); //Near the tip
- }
- else
- {
- //Near the edge ... return the corresponding point on the edge.
- float l = (float)sqrt(direction.x * direction.x + direction.y * direction.y);
- if (l < 0.01f)
- return Vector(0.0f, 0.0f, 0.0f);
- else
- {
- float xy = GetRadius() / l;
- return Vector(direction.x * xy, direction.y * xy, 0.0f);
- }
- }
- }
- };
- #undef new
- class SingleHull
- {
- public:
- SingleHull(int nVertices, const Vector& center)
- :
- m_nVertices(nVertices),
- m_center(center)
- {
- }
- void* operator new(size_t size, int nVertices, int nAdjacencies)
- {
- return (void*)(::new char [size +
- sizeof(ConvexVertex) * nVertices +
- sizeof(ConvexVertex*) * (nAdjacencies + nVertices)]);
- }
- const ConvexVertex* GetExtremeVertex(const Vector& direction) const
- {
- return m_pcvExtremes[((direction.x < 0.0) ? 0 : 1) +
- ((direction.y < 0.0) ? 0 : 2) +
- ((direction.z < 0.0) ? 0 : 4)];
- }
- void CalculateExtremeVertices(void)
- {
- static const float octants[][3] = {
- {-1.0f, -1.0f, -1.0f},
- { 1.0f, -1.0f, -1.0f},
- {-1.0f, 1.0f, -1.0f},
- { 1.0f, 1.0f, -1.0f},
- {-1.0f, -1.0f, 1.0f},
- { 1.0f, -1.0f, 1.0f},
- {-1.0f, 1.0f, 1.0f},
- { 1.0f, 1.0f, 1.0f}
- };
- //Pre-find the extreme vertices in each octant
- for (int octant = 0; (octant < 8); octant++)
- {
- const Vector& direction = *((Vector*)(octants[octant]));
- const ConvexVertex* pcv = vertex0();
- double dotMax = direction * pcv->m_position;
- while (true)
- {
- const ConvexVertex* pcvNext = NULL;
- const ConvexVertex** ppcvAdjacent = pcv->m_adjacencies;
- do
- {
- double dot = direction * (*ppcvAdjacent)->m_position;
- if (dot > dotMax)
- {
- dotMax = dot;
- pcvNext = (*ppcvAdjacent);
- }
- }
- while (*(++ppcvAdjacent) != NULL);
- if (pcvNext)
- pcv = pcvNext;
- else
- {
- m_pcvExtremes[octant] = pcv;
- break;
- }
- }
- }
- }
- const Vector& GetCenter(void) const
- {
- return m_center;
- }
- private:
- const ConvexVertex* vertex0(void) const
- {
- return ((const ConvexVertex*)(((char*)this) + sizeof(*this)));
- }
- const ConvexVertex** adjacency0(void) const
- {
- return ((const ConvexVertex**)(vertex0() + m_nVertices));
- }
- int m_nVertices;
- const ConvexVertex* m_pcvExtremes[8];
- Vector m_center;
- friend class BoundingHull;
- friend class HitTest;
- };
- class MultiHull : public MultiHullBase
- {
- public:
- MultiHull(int nHulls,
- float originalRadius,
- const Vector& ellipseEquation,
- float ellipseRadiusMultiplier)
- :
- MultiHullBase(ellipseEquation, originalRadius),
- m_nHulls(nHulls),
- //m_ellipseEquation(ellipseEquation),
- m_ellipseRadiusMultiplier(ellipseRadiusMultiplier)
- {
- }
- ~MultiHull(void)
- {
- for (int i = 0; (i < m_nHulls); i++)
- delete GetSingleHull(i);
- }
- void* operator new(size_t size, int nHulls)
- {
- return (void*)(::new char [size +
- sizeof(SingleHull*) * nHulls]);
- }
- int GetNhulls(void) const
- {
- return m_nHulls;
- }
- SingleHull* GetSingleHull(int hullID) const
- {
- assert (hullID >= 0);
- assert (hullID < m_nHulls);
- return hull0()[hullID];
- }
- private:
- SingleHull** hull0(void) const
- {
- return ((SingleHull**)(((char*)this) + sizeof(*this)));
- }
- int m_nHulls;
- float m_ellipseRadiusMultiplier; //>= m_originalRadius
- //Vector m_ellipseEquation; //(x/ee.x)^2 + (y/ee.y)^2 + (z/ee.z)^2 <= 1
- friend class BoundingHull;
- friend class HitTest;
- };
- class BoundingHull : public HitTest
- {
- public:
- BoundingHull(IObject* d,
- bool staticF,
- const MultiHull* pMultiHull)
- :
- HitTest(d, pMultiHull->m_originalRadius, staticF, pMultiHull->GetNhulls()),
- m_pMultiHull(pMultiHull)
- {
- const ConvexVertex** pcvRecent = recentVertex0();
- for (int i = pMultiHull->GetNhulls() - 1; (i >= 0); i--)
- pcvRecent[i] = pMultiHull->GetSingleHull(i)->vertex0();
- }
- void* operator new(size_t size, const MultiHull* pMultiHull)
- {
- return (void*)(::new char [size +
- sizeof(ConvexVertex*) * pMultiHull->GetNhulls()]);
- }
- virtual void UpdateStaticBB(void)
- {
- HitTest::UpdateStaticBB(m_desiredRadius * m_pMultiHull->m_ellipseRadiusMultiplier);
- }
- Vector GetCenter(HitTestShape hts) const
- {
- assert (hts >= c_htsConvexHullMin);
- return m_pMultiHull->GetSingleHull(hts)->GetCenter() * m_scale;
- }
- Vector GetEllipseExtreme(const Vector& direction)
- {
- //Get the extreme point of an ellipse in direction
- //The ellipse equation is: (x/a)^2 + (y/b)^2 + (z/c)^2 <= 1
- //
- //The normal to the ellipse at (x,y,z) is (x/a^2, y/b^2, z/c^2)
- //and the normal must be parallel to the direction. Therefore:
- // direction = Dxyz = k * (x/a^2, y/b^2, z/c^2)
- // x = a^2 Dx/k, y = b^2 Dy/k, z = c^2 Dz / k
- // and a^2 Dx^2 + b^2 Dy^2 + c^2 Dz^2 = k^2
- double a2 = m_ellipseEquation.x * m_ellipseEquation.x;
- double b2 = m_ellipseEquation.y * m_ellipseEquation.y;
- double c2 = m_ellipseEquation.z * m_ellipseEquation.z;
- double rk = 1.0 / sqrt(a2 * direction.x * direction.x +
- b2 * direction.y * direction.y +
- c2 * direction.z * direction.z);
- Vector extreme((float)(direction.x * a2 * rk),
- (float)(direction.y * b2 * rk),
- (float)(direction.z * c2 * rk));
- return extreme;
- }
- #pragma optimize ("p", on)
- Vector GetHullExtreme(int hullID, const Vector& direction)
- {
- assert (hullID >= 0);
- assert (hullID < m_pMultiHull->GetNhulls());
- assert (direction.LengthSquared() >= 0.98f);
- assert (direction.LengthSquared() <= 1.02f);
- assert (m_pMultiHull);
- const ConvexVertex** ppcvRecent = &(recentVertex0()[hullID]);
- assert (ppcvRecent);
- const ConvexVertex* pcv = *ppcvRecent;
- assert (pcv);
- float dotMax = direction * pcv->m_position;
- if (dotMax < 0.0)
- {
- pcv = m_pMultiHull->GetSingleHull(hullID)->GetExtremeVertex(direction);
- dotMax = direction * pcv->m_position;
- }
- int n = 0;
- while (true)
- {
- //Hack to verify that FPU round-off error will not cause an infinite loop in retail
- n++;
- ZRetailAssert (n < 300);
-
- const ConvexVertex* pcvNext = NULL;
- const ConvexVertex** ppcvAdjacent = pcv->m_adjacencies;
- do
- {
- float dot = direction * (*ppcvAdjacent)->m_position;
- #ifdef _M_IX86 // should only be a problem on x86 CPUs
- __asm lea eax, [dot];
- #endif
- if (dot > dotMax)
- {
- dotMax = dot;
- pcvNext = (*ppcvAdjacent);
- }
- }
- while (*(++ppcvAdjacent) != NULL);
- if (pcvNext)
- pcv = pcvNext;
- else
- {
- *ppcvRecent = pcv;
- return pcv->m_position * m_scale;
- }
- }
- }
- #pragma optimize ("", on)
- virtual void SetShape(HitTestShape shape)
- {
- //Never upgrade to anything more complex than our true shape
- HitTestShape s = (shape <= m_shapeTrue) ? shape : m_shapeTrue;
- if (s != m_shape)
- {
- m_shape = s;
- if (m_shape == c_htsEllipse)
- m_radius = m_desiredRadius * m_pMultiHull->m_ellipseRadiusMultiplier;
- else
- m_radius = m_desiredRadius;
- }
- }
- virtual void SetRadius(float r)
- {
- m_desiredRadius = r;
- m_scale = r / m_pMultiHull->m_originalRadius;
- m_ellipseEquation = m_scale * m_pMultiHull->GetEllipseEquation();
- //Add a fudge factor to the bounding sphere for an ellipse
- //which is generally larger than the bounding sphere for either
- //a sphere or a convex hull.
- if (m_shape == c_htsEllipse)
- m_radius = r * m_pMultiHull->m_ellipseRadiusMultiplier;
- else
- m_radius = r;
- }
- virtual float GetScale(void) const
- {
- return m_scale;
- }
- virtual const Vector* GetEllipseEquation(void) const
- {
- return &m_ellipseEquation;
- }
- const Vector GetFrameOffset(const char* pszFrameName) const
- {
- return m_pMultiHull->GetFrameOffset(pszFrameName) * m_scale;
- }
- const Vector& GetFrameForward(const char* pszFrameName) const
- {
- return m_pMultiHull->GetFrameOffset(pszFrameName);
- }
- const FrameDataUTL* GetFrame(const char* pszFrameName) const
- {
- return m_pMultiHull->GetFrame(pszFrameName);
- }
- private:
- const ConvexVertex** recentVertex0(void) const
- {
- return ((const ConvexVertex**)(((char*)this) + sizeof(*this)));
- }
- float m_desiredRadius;
- float m_scale;
- Vector m_ellipseEquation;
- const MultiHull* m_pMultiHull;
- };
- class CachedMultiHull
- {
- public:
- ~CachedMultiHull(void)
- {
- delete m_pMultiHull;
- }
- char m_name[c_cbName];
- MultiHull* m_pMultiHull;
- friend bool operator!=(const CachedMultiHull& r1, const CachedMultiHull& r2)
- {
- return false;
- }
-
- };
- typedef Slist_utl<CachedMultiHull> CachedList;
- typedef Slink_utl<CachedMultiHull> CachedLink;
- CachedList cachedMultiHulls;
- MultiHullBase* HitTest::Load(const char* pszFileName)
- {
- if ((!pszFileName) || (pszFileName[0] == '\0'))
- return NULL;
- MultiHull* pMultiHull = NULL;
- #ifndef DREAMCAST
- //Does the name exist in the cache?
- //It could exist but be a non-existant hull
- bool bFound = false;
- {
- for (CachedLink* pcl = cachedMultiHulls.first();
- (pcl != NULL);
- pcl = pcl->next())
- {
- const CachedMultiHull& cmh = pcl->data();
- if (_stricmp(pszFileName, cmh.m_name) == 0)
- {
- pMultiHull = cmh.m_pMultiHull;
- bFound = true;
- break;
- }
- }
- }
- if (!bFound)
- {
- assert (pMultiHull == NULL);
- //First time we've seen this file ... read it in.
- char artwork[MAX_PATH];
- HRESULT hr = UTL::getFile(pszFileName, ".cvh", artwork,
- true, true);
- if (hr == S_OK) //Don't bother to try and read a file that doesn't contain a valid convex hull
- {
- FILE* fileIn = fopen(artwork, "r");
- if (fileIn)
- {
- const int c_maxLine = 512;
- char line[c_maxLine];
- float radius;
- Vector ee; //ellipse equation
- float erm;
- if (fgets(line, c_maxLine, fileIn) &&
- (sscanf(line, "%f %f %f %f %f",
- &radius, &ee.x, &ee.y, &ee.z, &erm) == 5))
- {
- int nHulls;
- if (fgets(line, c_maxLine, fileIn) &&
- (sscanf(line, "%d",
- &nHulls) == 1))
- {
- assert (nHulls > 0);
- assert (nHulls < c_htsConvexHullMax);
- pMultiHull = new(nHulls) MultiHull(nHulls, radius, ee, erm);
- SingleHull** ppsh = pMultiHull->hull0();
- for (int hullID = nHulls - 1; (hullID >= 0); hullID--)
- {
- int nVertices;
- int nAdjacencies;
- Vector center;
- if (fgets(line, c_maxLine, fileIn) &&
- (sscanf(line, "%d %d %f %f %f",
- &nVertices, &nAdjacencies,
- &(center.x), &(center.y), &(center.z)) == 5))
- {
- assert (nVertices > 0);
- assert (nAdjacencies > 0);
- //Same LHS nonsense as for vertices.
- center.x = -center.x;
- SingleHull* psh = new(nVertices, nAdjacencies) SingleHull(nVertices, center);
- *(ppsh++) = psh;
- //Hack alert: force a cast away from constness so that we can modify the vertex
- //as we read it in.
- ConvexVertex* pcv = (ConvexVertex*)(psh->vertex0());
- const ConvexVertex** ppcvAdjacent = psh->adjacency0();
- //Read in the vertices and their adjacency information.
- do
- {
- Vector xyz;
- int n;
- if (fgets(line, c_maxLine, fileIn) &&
- (sscanf(line, "%f %f %f %d", &xyz.x, &xyz.y, &xyz.z, &n) == 4))
- {
- assert (n > 0);
- //NYI hack, hack, hack ... objects are yaw'd 180 degrees so apply the same
- //transformation the vertices in the convex hull
- xyz.x = -xyz.x;
- //Initialize the vertex
- pcv->SetPosition(xyz);
- pcv->m_adjacencies = ppcvAdjacent;
- //Read in the adjacent vertex information
- do
- {
- int id;
- if (fscanf(fileIn, "%d ", &id) == 1)
- {
- nAdjacencies--;
- assert (nAdjacencies >= 0);
- *(ppcvAdjacent++) = psh->vertex0() + id;
- }
- else
- assert (false);
- }
- while (--n);
- *(ppcvAdjacent++) = NULL;
- }
- else
- assert (false);
- pcv++;
- }
- while (--nVertices);
- assert (nAdjacencies == 0);
- psh->CalculateExtremeVertices();
- }
- }
- }
- }
- while (fgets(line, c_maxLine, fileIn))
- {
- FrameLink* pfl = new FrameLink;
- FrameDataUTL* pfd = &(pfl->data());
- assert (strlen(line) > 0);
- assert (strlen(line) < c_cbFrameName);
- line[strlen(line) - 1] = '\0'; //Trim off the \n
- strcpy(pfd->szName, line);
- ZVerify(fgets(line, c_maxLine, fileIn));
- sscanf(line, "%f %f %f",
- &(pfd->position.x),
- &(pfd->position.y),
- &(pfd->position.z));
- ZVerify(fgets(line, c_maxLine, fileIn));
- sscanf(line, "%f %f %f",
- &(pfd->forward.x),
- &(pfd->forward.y),
- &(pfd->forward.z));
- //Hack ... objects are flipped when they are read in so do it for the frames as well.
- pfd->position.x = -pfd->position.x;
- //Forward directions are completely reversed (and need X flipped ... so flip y & z)
- pfd->forward.y = -pfd->forward.y;
- pfd->forward.z = -pfd->forward.z;
- pMultiHull->m_frames.last(pfl);
- }
- fclose(fileIn);
- }
- }
- //Create a cache entry for this convex hull file (whether or not we were
- //actually able to load the .cvh file)
- {
- CachedLink* pcl = new CachedLink;
- CachedMultiHull& cmh = pcl->data();
- assert (strlen(pszFileName) < c_cbName);
- strcpy(cmh.m_name, pszFileName);
- cmh.m_pMultiHull = pMultiHull;
- //Put it at the front of the list (since we might read another one in real soon).
- cachedMultiHulls.first(pcl);
- }
- }
- #endif
- return pMultiHull;
- }
- HitTest* HitTest::Create(const char* pszFileName,
- IObject* data,
- bool staticF,
- HitTestShape htsDefault)
- {
- HitTest* pHitTest = NULL;
- #ifndef DREAMCAST
- if (pszFileName)
- {
- //An unsafe upcast ... but we know better.
- MultiHull* pMultiHull = (MultiHull*)Load(pszFileName);
- if (pMultiHull)
- {
- pHitTest = new (pMultiHull) BoundingHull(data, staticF, pMultiHull);
- }
- }
- #endif //dreamcast
- if (!pHitTest)
- {
- switch (htsDefault)
- {
- case c_htsSphere:
- pHitTest = (HitTest*)(new BoundingSphere(data, staticF));
- break;
- case c_htsPoint:
- pHitTest = (HitTest*)(new BoundingPoint(data, staticF));
- break;
- case c_htsCone:
- pHitTest = (HitTest*)(new BoundingCone(data, staticF));
- break;
- default:
- assert (false);
- }
- }
- return pHitTest;
- }
- Vector HitTest::GetMinExtreme(HitTestShape hts,
- const Vector& position,
- const Orientation& orientation,
- const Vector& direction)
- {
- switch (hts)
- {
- case c_htsPoint:
- return position;
- case c_htsEllipse:
- return position + ((BoundingHull*)this)->GetEllipseExtreme(orientation.TimesInverse(-direction)) * orientation;
- case c_htsCone:
- return position + ((BoundingCone*)this)->GetExtreme(orientation.TimesInverse(-direction)) * orientation;
- case c_htsSphere:
- return position - direction * m_radius;
- default:
- return position + ((BoundingHull*)this)->GetHullExtreme(hts, orientation.TimesInverse(-direction)) * orientation;
- }
- }
- Vector HitTest::GetMaxExtreme(HitTestShape hts,
- const Vector& direction)
- {
- switch (hts)
- {
- case c_htsPoint:
- return Vector::GetZero();
- case c_htsEllipse:
- return ((BoundingHull*)this)->GetEllipseExtreme(direction);
- case c_htsCone:
- return ((BoundingCone*)this)->GetExtreme(direction);
- case c_htsSphere:
- return direction * m_radius;
- default:
- return ((BoundingHull*)this)->GetHullExtreme(hts, direction);
- }
- }
- static bool DoGilbert(HitTest* phtObjectA,
- HitTestShape htsA,
- const Vector& positionStart,
- const Vector& positionStop,
- const Vector& dV,
- const Orientation& orientationHullA,
- HitTest* phtObjectB,
- HitTestShape htsB,
- Vector simplex[4]);
- bool HitTest::HullCollide(float* tStart,
- float tMax,
- HitTest* phtHullA,
- HitTestShape* phtsA,
- HitTest* phtHullB,
- HitTestShape* phtsB,
- const Vector& dP,
- const Vector& dV)
- {
- assert (tStart);
- assert (phtHullA);
- assert (phtHullB);
- //In general, phtHullB is more complex than phtHullA ... so optimize things to minimize the manipulations of phtHullB
- //In particular, re-orient everything so that we are in B's local coordinate space (dP & dV are aready wrt B).
- const Orientation& oB = phtHullB->GetOrientation();
- //A common special case is a c_htsPoint vs. c_htsEllipse, so test that explicitly
- if ((phtHullA->m_shape == c_htsPoint) && (phtHullB->m_shape == c_htsEllipse))
- {
- Vector p = oB.TimesInverse(dP - *tStart * dV);
- //Point vs. ellipse ... distort the coordinate system based on the ellipse equation of phtHullB (which we can do since we
- //are in phtHullB's local coordinate system).
- const Vector& ee = *(phtHullB->GetEllipseEquation());
- //Now ... when does a point starting at p and moving -v intersect a sphere with radius 1.0 around the origin
- //P(t) = p - v * t; P(t)^2 = 1 = p*p - 2v*p*t + v*v*t^2
- //
- //v*v*t^2 - 2v*p*t + p*p-1 = 0
- //(v*v/2) t^2 - v*p t + ((p*p-1)/2) = 0
- //
- //(a/2) t^2 - b t + (c/2) = 0
- // t = (b +- sqrt(b*b-4(a/2)(c/2))) / (2 * (a/2)) = (b +- sqrt(b*b-ac)) / a
- p.x /= ee.x;
- p.y /= ee.y;
- p.z /= ee.z;
- double c = p * p - 1.0;
- if (c <= 0.0)
- {
- //Special case ... point is on or inside the ellipse at the start of the interval
- *phtsA = phtHullA->m_shape;
- *phtsB = phtHullB->m_shape;
- return true;
- }
- //c > 0
- Vector v = oB.TimesInverse(dV);
- v.x /= ee.x;
- v.y /= ee.y;
- v.z /= ee.z;
- double b = v * p;
- if (b > 0.0)
- {
- //The point is moving closer to the ellipse ... continue
- double a = v * v; //>= 0
- double bac = b*b - a * c; //a*c >= 0.0 so bac <= b*b
- if (bac >= 0.0)
- {
- //Starting from *tStart so ... calculate time time of collision appropriately
- float t = *tStart + float((b - sqrt(bac)) / a);
- if (t <= tMax)
- {
- *tStart = t;
- *phtsA = phtHullA->m_shape;
- *phtsB = phtHullB->m_shape;
- return true;
- }
- }
- }
- return false;
- }
- else
- {
- Vector p = oB.TimesInverse(dP);
- Vector v = oB.TimesInverse(dV);
- const Orientation& oA = phtHullA->GetOrientation();
- Orientation o = oA.TimesInverse(oB); // == oA * oB^-1
- const double c_maxDelta = 0.25;
- double speed = dV.Length();
- //Hack alert ... only phtHullB should be subject to the on the fly adjustment
- //of the hit test shape ... so fiddle with their shape.
- HitTestShape htsA = phtHullA->m_shape;
- HitTestShape htsB = ((phtHullB->m_pvUseTrueShapeSelf == NULL) ||
- (phtHullB->m_pvUseTrueShapeSelf != phtHullA->m_pvUseTrueShapeOther)) ? phtHullB->m_shape : phtHullB->m_shapeTrue;
- HitTestShape htsAcurrent = (htsA < 0) ? htsA : 0;
- float tOriginalStart = *tStart;
- bool bCollision = false;
- do
- {
- HitTestShape htsBcurrent = (htsB < 0) ? htsB : 0;
- do
- {
- if (HitTest::IntervalCollide(tOriginalStart, tMax, (speed == 0.0) ? FLT_MAX : (float)(c_maxDelta / speed),
- phtHullA, htsAcurrent,
- phtHullB, htsBcurrent,
- p, v, o,
- tStart))
- {
- bCollision = true;
- *phtsA = htsAcurrent;
- *phtsB = htsBcurrent;
- tMax = *tStart; //No point in worrying about collisions that happen after colliding with another part
- }
- }
- while (++htsBcurrent < htsB);
- }
- while (++htsAcurrent < htsA);
- return bCollision;
- }
- }
- bool HitTest::IntervalCollide(float tStart,
- float tStop,
- float maxDeltaT,
- HitTest* phtHullA,
- HitTestShape htsA,
- HitTest* phtHullB,
- HitTestShape htsB,
- const Vector& dP,
- const Vector& dV,
- const Orientation& orientationA,
- float* tCollision)
- {
- float tMiddle = (tStart + tStop) / 2.0f;
- //Do the two convex hulls intersect anywhere along the interval between *tCollision and tMax?
- Vector direction = dP - tMiddle * dV;
- if (htsA >= c_htsConvexHullMin)
- direction += phtHullA->GetCenter(htsA) * orientationA;
- if (htsB >= c_htsConvexHullMin)
- direction -= phtHullB->GetCenter(htsB);
- if ((direction.x == 0.0f) && (direction.y == 0.0f) && (direction.z == 0.0f))
- return true;
- direction.SetNormalize();
- Vector positionStart = (dP - tStart * dV);
- Vector positionStop = (dP - tStop * dV);
- //Try 4 times, saving each result to see if we
- //can find a plane that clearly puts the sphere
- //outside the hull
- const int c_maxAttempts = 4;
- Vector extremesHullB[c_maxAttempts];
- Vector extremesHullA[c_maxAttempts];
- int attempt = 0;
- while (true)
- {
- //For hull A, the extreme is found by takeing the extreme in the given direction and then
- //offsetting it by the position at start or the end of the interval (which ever is appropriate:
- //there are two sets of negation cancelling out here).
- extremesHullA[attempt] = phtHullA->GetMinExtreme(htsA, (direction * dV) >= 0.0f ? positionStop : positionStart,
- orientationA, direction);
- extremesHullB[attempt] = phtHullB->GetMaxExtreme(htsB, direction);
- Vector delta = extremesHullA[attempt] - extremesHullB[attempt];
- double distance = (delta * direction);
- if (distance > 0.0)
- {
- //There is no collision anywyare along the interval ... bye
- return false;
- }
- else if (attempt++ >= c_maxAttempts - 1)
- break;
- //Didn't find a good separating plane ... munge direction in the hopes of finding a better one
- //reflect direction around the vector from the extreme to the sphere
- //Get the vector from direction to its projection onto -delta
- direction -= delta * (2.0f * (direction * delta) / delta.LengthSquared());
- assert (direction.Length() >= 0.98f);
- assert (direction.Length() <= 1.02f);
- }
- //We made 4 attempts to find a separating plane and failed.
- //Invoke Gilbert's algorithm.
- Vector simplex[4];
- {
- int i = 0;
- do
- simplex[i] = extremesHullA[i] - extremesHullB[i];
- while (i++ < 3);
- }
- if (!DoGilbert(phtHullA, htsA, positionStart, positionStop, dV, orientationA,
- phtHullB, htsB, simplex))
- {
- //No collision ... also bye
- return false;
- }
- //We have a collision somewhere along the interval ...
- if (tStop - tStart <= maxDeltaT)
- {
- //The interval is small enough that we really do not care any more.
- *tCollision = tMiddle;
- return true;
- }
- else //split the interval in two and try again
- return IntervalCollide(tStart, tMiddle, maxDeltaT, phtHullA, htsA, phtHullB, htsB, dP, dV, orientationA, tCollision) ||
- IntervalCollide(tMiddle, tStop, maxDeltaT, phtHullA, htsA, phtHullB, htsB, dP, dV, orientationA, tCollision);
- }
- static int closest_vertex(int v0,
- const Vector p[],
- //double lambda[],
- int index[],
- Vector* cp)
- {
- //lambda[0] = 1;
- index[0] = v0;
- *cp = p[v0];
- return 1;
- }
- static int closest_edge(double e0,
- double e1,
- int ie0,
- int ie1,
- const Vector p[],
- int index[],
- Vector* cp)
- {
- double esum_inv = 1.0f/(e0+e1);
- float lambda[2];
- lambda[0] = (float)(e0*esum_inv);
- lambda[1] = (float)(e1*esum_inv);
- index[0] = ie0;
- index[1] = ie1;
- *cp = lambda[0] * p[ie0] + lambda[1] * p[ie1];
- return 2;
- }
- static int closest_face(double f0,
- double f1,
- double f2,
- int if0,
- int if1,
- int if2,
- const Vector p[],
- //double lambda[],
- int index[],
- Vector* cp)
- {
- double fsum_inv = 1.0 / (f0+f1+f2);
- float lambda[3];
- lambda[0] = (float)(f0*fsum_inv);
- lambda[1] = (float)(f1*fsum_inv);
- lambda[2] = (float)(f2*fsum_inv);
- index[0] = if0;
- index[1] = if1;
- index[2] = if2;
- *cp = lambda[0] * p[if0] +
- lambda[1] * p[if1] +
- lambda[2] * p[if2];
- return 3;
- }
- /*
- Johnson algorithm for tetrahedron
- Johnson's algorithm for finding the closest point to the origin on a simplex (3d).
- Inputs:
- p -- array of n 3d points forming the simplex
- n -- number of points, must be 1, 2, 3, or 4.
- Outputs:
- cp -- closest point to origin on simplex
- index -- indices of simplex points forming feature closest to origin {in increasing order]
- lambda -- coefficients of above indexed points forming cp
- Each coefficient is nonnegative and their sum is 1.
- Returns: number of simplex points forming closest feature:
- 1 = vertex
- 2 = edge
- 3 = face
- 4 = tetrahedron (inside simplex interior)
- The return value is always <= n and >= 1.
- */
- static int Johnson2(const Vector p[2],
- Vector* cp,
- int index[])
- {
- Vector d = p[1] - p[0];
- double l0 = p[0] * d;
- if (l0 >= 0)
- {
- *cp = p[0];
- index[0] = 0;
- return 1;
- }
- double l1 = p[1] * d; //Note ... sense is reversed wrt l0
- if (l1 <= 0)
- {
- *cp = p[1];
- index[0] = 1;
- return 1;
- }
- index[0] = 0;
- index[1] = 1;
- *cp = p[0] + (float)(l0 / (l0 - l1)) * d;
- return 2;
- }
- /*
- #define DOT(a,b) ( (a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] )
- static
- int jsny3(double *p,double cp[3], int index[])
- {
- double d[3][3]; // array of dot products d[i][j] = <p[i],p[j]>
- double d2[3][2]; // array of D_k{i,j} encoded as [l][0 if k=i, 1 if k=j] i < j, k in {i,j}
- // where l=0 -> {0,1}, l=1 -> {1,2}, l=2 -> {0,2}
- double d3[3]; // array of D_l{i,j,k} encoded as [l], i < j < k, l in {i,j,k}
- register double *pi,*pj;
- register int i,j;
- //double min;
- //int n_min;
- // compute dot products
- pi = p;
- for (i = 0; i < 3; i++) {
- pj = pi;
- for (j = i; j < 3; j++) {
- d[i][j] = d[j][i] = DOT(pi,pj);
- pj += 3;
- }
- pi += 3;
- }
- // compute D2
- d2[0][0] = d[1][1] - d[1][0];
- d2[0][1] = d[0][0] - d[0][1];
- d2[1][0] = d[2][2] - d[2][1];
- d2[1][1] = d[1][1] - d[1][2];
- d2[2][0] = d[2][2] - d[2][0];
- d2[2][1] = d[0][0] - d[0][2];
- // compute D3
- d3[0] = d2[1][0]*(d[1][1]-d[1][0]) + d2[1][1]*(d[2][1]-d[2][0]);
- d3[1] = d2[2][0]*(d[0][0]-d[0][1]) + d2[2][1]*(d[2][0]-d[2][1]);
- d3[2] = d2[0][0]*(d[0][0]-d[0][2]) + d2[0][1]*(d[1][0]-d[1][2]);
- debugf("jsny3\n");
- {
- debugf("p[]\n");
- for (int i = 0; (i < 3); i++)
- debugf("%f %f %f\n", p[i*3+0], p[i*3+1], p[i*3+2]);
- debugf("d[][]\n");
- for (int j = 0; (j < 3); j++)
- debugf("%f %f %f\n", d[j][0], d[j][1], d[j][2]);
- debugf("d2[][]\n");
- for (int k = 0; (k < 3); k++)
- debugf("%f %f\n", d2[k][0], d2[k][1]);
- debugf("d3\n");
- debugf("%f %f %f\n", d3[0], d3[1], d3[2]);
- }
- return 0;
- }
- */
- static int Johnson3(const Vector p[3],
- Vector* cp,
- int index[])
- {
- //Mappings between all possible subsets of the vertices and a
- //unique subset ID.
- // subset1[i] ({Pi})
- // subset2[i][j] ({Pi, Pj}) i != j
- // subset3 ({P0, P1, P2})
- static const int subset1[3] = { 0, 1, 2};
- static const int subset2[3][3] = {{-1, 3, 4},
- { 3, -1, 5},
- { 4, 5, -1}};
- static const int subset3 = 6;
- double dp[3][3];
- {
- for (int i = 0; (i < 3); i++)
- for (int j = i; (j < 3); j++)
- dp[i][j] = dp[j][i] = (double)(p[i].x) * (double)(p[j].x) +
- (double)(p[i].y) * (double)(p[j].y) +
- (double)(p[i].z) * (double)(p[j].z);
- }
- double delta[3][subset3 + 1];
- //Generic form: delta[i][{Pi}] = 1.0
- // but don't bother
- {
- //Generic form: delta[i][{Pj} | {Pi}] = Pj * (Pj - Pi) [i != j]
- // = dp[j][j] - dp[j][i]
- #define SetDelta(i,j) delta[i][subset2[j][i]] = dp[j][j] - dp[j][i]
- SetDelta(1, 0);
- SetDelta(2, 0);
- SetDelta(0, 1);
- SetDelta(2, 1);
- SetDelta(0, 2);
- SetDelta(1, 2);
- #undef SetDelta
- }
- {
- //Generic form: delta[i][{Pj, Pk} | {Pi}] = delta[j][{Pj, Pk}] * (Pj * (Pj - Pi)) + [i != j, k; j < k]
- // delta[k][{Pj, Pk}] * (Pk * (Pj - Pi))
- // = delta[j][{Pj, Pk}] * (dp[j][j] - dp[j][i]) +
- // delta[k][{Pj, Pk}] * (dp[k][j] - dp[k][i])
- // i != j, k; j < k; x != i, j, k
- #define SetDelta(i, j, k) delta[i][subset3] = (delta[j][subset2[j][k]] * (dp[j][j] - dp[j][i])) + \
- (delta[k][subset2[j][k]] * (dp[k][j] - dp[k][i]))
- SetDelta(2, 0, 1);
- SetDelta(1, 0, 2);
- SetDelta(0, 1, 2);
- #undef SetDelta
- }
- //Now that we've done all this ... see if we have a solution
- //We have a valid solution if:
- // delta[i][S] > 0 (for all i in S)
- // delta[j][S | {Pj}] <= 0 (for all j not in S)
- /*
- {
- debugf("p[]\n");
- for (int i = 0; (i < 3); i++)
- debugf("%f %f %f\n", p[i].x, p[i].y, p[i].z);
- debugf("dp[][]\n");
- for (int j = 0; (j < 3); j++)
- debugf("%f %f %f\n", dp[j][0], dp[j][1], dp[j][2]);
- debugf("delta[][]\n");
- debugf("%f %f\n", delta[0][subset2[0][1]], delta[1][subset2[0][1]]);
- debugf("%f %f\n", delta[1][subset2[1][2]], delta[2][subset2[1][2]]);
- debugf("%f %f\n", delta[2][subset2[0][2]], delta[2][subset2[0][2]]);
- debugf("d3\n");
- debugf("%f %f %f\n", delta[0][subset3], delta[1][subset3], delta[2][subset3]);
- }
- */
- //Inside for the triangle. The last clause of the test is degenerate
- //since there is no Pj not in S
- if ((delta[0][subset3] > 0.0) &&
- (delta[1][subset3] > 0.0) &&
- (delta[2][subset3] > 0.0))
- {
- //Yes
- return closest_face(delta[0][subset3], delta[1][subset3], delta[2][subset3],
- 0, 1, 2, p, index, cp);
- }
- {
- //Test for each vertex. The first clause of the test is degerate
- //since delta[i][S[i]] = 1
-
- //Test for vertex i
- #define TestDelta(i, j, k) if ((delta[i][subset2[j][i]] <= 0.0) && \
- (delta[i][subset2[k][i]] <= 0.0)) \
- return closest_vertex(i, p, index, cp);
- TestDelta(0, 1, 2);
- TestDelta(1, 0, 2);
- TestDelta(2, 0, 1);
- #undef TestDelta
- }
- {
- //Test for each edge i, j
- // delta[i][subset2[i][j]] > 0
- // delta[j][subset2[i][j]] > 0
- //
- // delta[k][subset3[l]] < 0
- // delta[l][subset3[k]] < 0
- #define TestDelta(i, j, k) if ((delta[i][subset2[i][j]] > 0.0) && \
- (delta[j][subset2[i][j]] > 0.0) && \
- (delta[k][subset3] <= 0.0)) \
- return closest_edge(delta[i][subset2[i][j]], \
- delta[j][subset2[i][j]], \
- i, j, p, index, cp)
- TestDelta(0, 1, 2);
- TestDelta(0, 2, 1);
- TestDelta(1, 2, 0);
- #undef TestDelta
- }
- {
- //Test for each face i, j, k
- // delta[i][subset3[l]] > 0
- // delta[j][subset3[l]] > 0
- // delta[k][subset3[l]] > 0
- //
- // delta[l][subset4] <= 0.0f
- #define TestDelta(i, j, k) if ((delta[i][subset3] > 0.0) && \
- (delta[j][subset3] > 0.0) && \
- (delta[k][subset3] > 0.0)) \
- return closest_face(delta[i][subset3], \
- delta[j][subset3], \
- delta[k][subset3], \
- i, j, k, p, index, cp)
- TestDelta(0, 1, 2);
- #undef TestDelta
- }
- //If we haven't found a solution so far (round off or some such) ...
- int nMin;
- double dMin2 = HUGE_VAL;
- {
- //Try the vertices
- int v;
- for (int i = 0; (i < 3); i++)
- {
- if (dp[i][i] < dMin2)
- {
- dMin2 = dp[i][i];
- v = i;
- }
- }
- nMin = closest_vertex(v, p, index, cp);
- }
- {
- //Try the edges
- for (int i = 0; (i < 2); i++)
- {
- for (int j = i + 1; (j < 3); j++)
- {
- if ((delta[i][subset2[i][j]] > 0.0) &&
- (delta[j][subset2[i][j]] > 0.0))
- {
- double d = (delta[i][subset2[i][j]] * dp[i][i] +
- delta[j][subset2[i][j]] * dp[j][i]) /
- (delta[i][subset2[i][j]] + delta[j][subset2[i][j]]);
- if (d < dMin2)
- {
- dMin2 = d;
- nMin = closest_edge(delta[i][subset2[i][j]],
- delta[j][subset2[i][j]],
- i, j, p, index, cp);
- }
- }
- }
- }
- }
- {
- if ((delta[0][subset3] > 0.0) &&
- (delta[1][subset3] > 0.0) &&
- (delta[2][subset3] > 0.0))
- {
- double d = (delta[0][subset3] * dp[0][0] +
- delta[1][subset3] * dp[1][0] +
- delta[2][subset3] * dp[2][0]) /
- (delta[0][subset3] +
- delta[1][subset3] +
- delta[2][subset3]);
- if (d < dMin2)
- {
- dMin2 = d;
- nMin = closest_face(delta[0][subset3],
- delta[1][subset3],
- delta[2][subset3],
- 0, 1, 2, p, index, cp);
- }
- }
- }
- return nMin;
- }
- static int Johnson4(const Vector p[4],
- Vector* cp,
- int index[])
- {
- //Mappings between all possible subsets of the vertices and a
- //unique subset ID.
- // subset1[i] ({Pi})
- // subset2[i][j] ({Pi, Pj}), i != j
- // subset3[i] ({Pj, Pk, Pl}), i != j; i != k; i != l
- // subset4 ({P0, P1, P2, P3})
- static const int subset1[4] = { 0, 1, 2, 3};
- static const int subset2[4][4] = {{-1, 4, 5, 6},
- { 4, -1, 7, 8},
- { 5, 7, -1, 9},
- { 6, 8, 9, -1}};
- static const int subset3[4] = {10, 11, 12, 13};
- static const int subset4 = 14;
- double dp[4][4];
- {
- for (int i = 0; (i < 4); i++)
- for (int j = i; (j < 4); j++)
- dp[i][j] = dp[j][i] = (double)(p[i].x) * (double)(p[j].x) +
- (double)(p[i].y) * (double)(p[j].y) +
- (double)(p[i].z) * (double)(p[j].z);
- }
- double delta[4][subset4 + 1];
- //Generic form: delta[i][{Pi}] = 1.0
- // but don't bother
- {
- //Generic form: delta[i][{Pj} | {Pi}] = Pj * (Pj - Pi) [i != j]
- // = dp[j][j] - dp[j][i]
- #define SetDelta(i,j) delta[i][subset2[j][i]] = dp[j][j] - dp[j][i]
- SetDelta(1, 0);
- SetDelta(2, 0);
- SetDelta(3, 0);
- SetDelta(0, 1);
- SetDelta(2, 1);
- SetDelta(3, 1);
- SetDelta(0, 2);
- SetDelta(1, 2);
- SetDelta(3, 2);
- SetDelta(0, 3);
- SetDelta(1, 3);
- SetDelta(2, 3);
- #undef SetDelta
- }
- {
- //Generic form: delta[i][{Pj, Pk} | {Pi}] = delta[j][{Pj, Pk}] * (Pj * (Pj - Pi)) + [i != j, k; j < k]
- // delta[k][{Pj, Pk}] * (Pk * (Pj - Pi))
- //
- // = delta[j][{Pj, Pk}] * (dp[j][j] - dp[j][i]) +
- // delta[k][{Pj, Pk}] * (dp[k][j] - dp[k][i])
- // i != j, k; j < k; x != i, j, k
- #define SetDelta(i, j, k, x) delta[i][subset3[x]] = (delta[j][subset2[j][k]] * (dp[j][j] - dp[j][i])) + \
- (delta[k][subset2[j][k]] * (dp[k][j] - dp[k][i]))
- SetDelta(2, 0, 1, 3);
- SetDelta(3, 0, 1, 2);
- SetDelta(1, 0, 2, 3);
- SetDelta(3, 0, 2, 1);
- SetDelta(1, 0, 3, 2);
- SetDelta(2, 0, 3, 1);
- SetDelta(0, 1, 2, 3);
- SetDelta(3, 1, 2, 0);
- SetDelta(0, 1, 3, 2);
- SetDelta(2, 1, 3, 0);
- SetDelta(0, 2, 3, 1);
- SetDelta(1, 2, 3, 0);
- #undef SetDelta
- }
- {
- //Generic form: delta[i][{Pj, Pk, Pl} | {Pi}] = delta[j][{Pj, Pk, Pl}] * (Pj * (Pj - Pi)) + [i != j, k, l; j < k, l, k != l]
- // delta[k][{Pj, Pk, Pl}] * (Pk * (Pj - Pi)) +
- // delta[l][{Pj, Pk, Pl}] * (Pl * (Pj - Pi))
- // = delta[j][{Pj, Pk, Pl}] * (dp[j][j] - dp[j][i]) +
- // = delta[j][{Pj, Pk, Pl}] * (dp[k][j] - dp[k][i]) +
- // = delta[j][{Pj, Pk, Pl}] * (dp[k][j] - dp[l][i])
- // i != j, k, l; j < k, l, k != l
- #define SetDelta(i, j, k, l) delta[i][subset4] = delta[j][subset3[i]] * (dp[j][j] - dp[j][i]) + \
- delta[k][subset3[i]] * (dp[k][j] - dp[k][i]) + \
- delta[l][subset3[i]] * (dp[l][j] - dp[l][i])
- SetDelta(0, 1, 2, 3);
- SetDelta(1, 0, 2, 3);
- SetDelta(2, 0, 1, 3);
- SetDelta(3, 0, 1, 2);
- #undef SetDelta
- }
- //Now that we've done all this ... see if we have a solution
- //We have a valid solution if:
- // delta[i][S] > 0 (for all i in S)
- // delta[j][S | {Pj}] <= 0 (for all j not in S)
- //Inside for the tetrahedron. The last clause of the test is degenerate
- //since there is no Pj not in S
- if ((delta[0][subset4] > 0.0) &&
- (delta[1][subset4] > 0.0) &&
- (delta[2][subset4] > 0.0) &&
- (delta[3][subset4] > 0.0))
- {
- //Yes
- *cp = Vector::GetZero();
- return 4;
- }
- {
- //Test for each vertex. The first clause of the test is degerate
- //since delta[i][S[i]] = 1
-
- //Test for vertex i
- #define TestDelta(i, j, k, l) if ((delta[i][subset2[j][i]] <= 0.0) && \
- (delta[i][subset2[k][i]] <= 0.0) && \
- (delta[i][subset2[l][i]] <= 0.0)) \
- return closest_vertex(i, p, index, cp);
- TestDelta(0, 1, 2, 3);
- TestDelta(1, 0, 2, 3);
- TestDelta(2, 0, 1, 3);
- TestDelta(3, 0, 1, 2);
- #undef TestDelta
- }
- {
- //Test for each edge i, j
- // delta[i][subset2[i][j]] > 0
- // delta[j][subset2[i][j]] > 0
- //
- // delta[k][subset3[l]] < 0
- // delta[l][subset3[k]] < 0
- #define TestDelta(i, j, k, l) if ((delta[i][subset2[i][j]] > 0.0) && \
- (delta[j][subset2[i][j]] > 0.0) && \
- (delta[k][subset3[l]] <= 0.0) && \
- (delta[l][subset3[k]] <= 0.0)) \
- return closest_edge(delta[i][subset2[i][j]], \
- delta[j][subset2[i][j]], \
- i, j, p, index, cp)
- TestDelta(0, 1, 2, 3);
- TestDelta(0, 2, 1, 3);
- TestDelta(0, 3, 1, 2);
- TestDelta(1, 2, 0, 3);
- TestDelta(1, 3, 0, 2);
- TestDelta(2, 3, 0, 1);
- #undef TestDelta
- }
- {
- //Test for each face i, j, k
- // delta[i][subset3[l]] > 0
- // delta[j][subset3[l]] > 0
- // delta[k][subset3[l]] > 0
- //
- // delta[l][subset4] <= 0.0f
- #define TestDelta(i, j, k, l) if ((delta[i][subset3[l]] > 0.0) && \
- (delta[j][subset3[l]] > 0.0) && \
- (delta[k][subset3[l]] > 0.0) && \
- (delta[l][subset4] <= 0.0)) \
- return closest_face(delta[i][subset3[l]], \
- delta[j][subset3[l]], \
- delta[k][subset3[l]], \
- i, j, k, p, index, cp)
- TestDelta(0, 1, 2, 3);
- TestDelta(0, 1, 3, 2);
- TestDelta(0, 2, 3, 1);
- TestDelta(1, 2, 3, 0);
- #undef TestDelta
- }
- //If we haven't found a solution so far (round off or some such) ...
- int nMin;
- double dMin2 = HUGE_VAL;
- {
- //Try the vertices
- int v;
- for (int i = 0; (i < 4); i++)
- {
- if (dp[i][i] < dMin2)
- {
- dMin2 = dp[i][i];
- v = i;
- }
- }
- nMin = closest_vertex(v, p, index, cp);
- }
- {
- //Try the edges
- for (int i = 0; (i < 3); i++)
- {
- for (int j = i + 1; (j < 4); j++)
- {
- if ((delta[i][subset2[i][j]] > 0.0) &&
- (delta[j][subset2[i][j]] > 0.0))
- {
- double d = (delta[i][subset2[i][j]] * dp[i][i] +
- delta[j][subset2[i][j]] * dp[j][i]) /
- (delta[i][subset2[i][j]] + delta[j][subset2[i][j]]);
- if (d < dMin2)
- {
- dMin2 = d;
- nMin = closest_edge(delta[i][subset2[i][j]],
- delta[j][subset2[i][j]],
- i, j, p, index, cp);
- }
- }
- }
- }
- }
- {
- {
- //Try the faces
- const int ids[4][4] = {{0, 1, 2, 3}, {0, 1, 3, 2},
- {0, 2, 3, 1}, {1, 2, 3, 0}};
- for (int f = 0; (f < 4); f++)
- {
- int i = ids[f][0];
- int j = ids[f][1];
- int k = ids[f][2];
- int l = ids[f][3];
- if ((delta[i][subset3[l]] > 0.0) &&
- (delta[j][subset3[l]] > 0.0) &&
- (delta[k][subset3[l]] > 0.0))
- {
- double d = (delta[i][subset3[l]] * dp[i][i] +
- delta[j][subset3[l]] * dp[j][i] +
- delta[k][subset3[l]] * dp[k][i]) /
- (delta[i][subset3[l]] +
- delta[j][subset3[l]] +
- delta[k][subset3[l]]);
- if (d < dMin2)
- {
- dMin2 = d;
- nMin = closest_face(delta[i][subset3[l]],
- delta[j][subset3[l]],
- delta[k][subset3[l]],
- i, j, k, p, index, cp);
- }
- }
- }
- }
- }
- return nMin;
- }
- int Johnson(int n,
- const Vector p[],
- Vector* cp,
- int index[])
- {
- switch (n)
- {
- case 1:
- {
- *cp = p[0];
- index[0] = 0;
- return 1;
- }
- case 2:
- {
- return Johnson2(p, cp, index);
- }
-
- case 3:
- {
- return Johnson3(p, cp, index);
- }
- default:
- {
- return Johnson4(p, cp, index);
- }
- }
- }
- static bool DoGilbert(HitTest* phtHullA,
- HitTestShape htsA,
- const Vector& positionStart,
- const Vector& positionStop,
- const Vector& dV,
- const Orientation& orientationHullA,
- HitTest* phtHullB,
- HitTestShape htsB,
- Vector simplex[4])
- {
- int nVertices = 3; //Always start with four (but the three is incremented before it is used).
- static const int c_maxHistory = 100;
- static Vector vertexHistory[c_maxHistory];
- vertexHistory[0] = simplex[0];
- vertexHistory[1] = simplex[1];
- vertexHistory[2] = simplex[2];
- vertexHistory[3] = simplex[3];
- int vertexID = 3;
- Vector cp;
- Vector delta;
- while (true)
- {
- int indices[3];
- int nMin = Johnson(++nVertices, simplex, &cp, indices);
- if (nMin == 4)
- {
- return true;
- }
- //Find a new extreme point
- {
- double l2 = cp.LengthSquared();
- if (l2 < epsilon * epsilon)
- return true;
- cp /= (float)sqrt(l2);
- }
- Vector v0 = phtHullA->GetMinExtreme(htsA, (cp * dV >= 0.0f) ? positionStop : positionStart, orientationHullA, cp) -
- phtHullB->GetMaxExtreme(htsB, cp);
- if ((v0 * cp) > 0.0)
- {
- //Found a separating plane
- return false;
- }
- //Have we been here before?
- {
- for (int i = vertexID; (i >= 0); i--)
- {
- if ((vertexHistory[i] - v0).LengthSquared() < epsilon * epsilon)
- {
- //A duplicate point ... so the simplex will not contain the origin.
- return false;
- }
- }
- assert (vertexID < c_maxHistory - 1);
- vertexHistory[++vertexID] = v0;
- }
- //Otherwise ... eliminate all the points from the simplex
- //that are not in the index array (assume the index array
- //is sorted).
- {
- int i = 0;
- do
- {
- if (indices[i] != i)
- {
- assert (indices[i] > i);
- simplex[i] = simplex[indices[i]];
- }
- }
- while (++i < nMin);
- nVertices = i;
- }
- //Replace the last vertex in the simplex array with new vertex
- simplex[nVertices] = v0;
- }
- return false;
- }
|