You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3108 lines
69 KiB
3108 lines
69 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, 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/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
|
|
#include "hsTypes.h" |
|
#include "hsBounds.h" |
|
#include "hsStream.h" |
|
|
|
#include "hsFastMath.h" |
|
|
|
#if defined(__MWERKS__) && !defined(HS_DEBUGGING) |
|
#pragma optimization_level 2 |
|
#endif |
|
|
|
const hsScalar hsBounds::kRealSmall = 1.0e-5f; |
|
|
|
/////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// hsBounds |
|
// |
|
///////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
void hsBounds::Read(hsStream *s) |
|
{ |
|
fType =(hsBoundsType) s->ReadSwap32(); |
|
} |
|
|
|
void hsBounds::Write(hsStream *s) |
|
{ |
|
s->WriteSwap32((Int32)fType); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// hsBounds3 |
|
// |
|
///////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
#if 0 // MESH_GEN_DEFER |
|
void hsBounds3::Draw(hsGView3* v, hsG3DDevice* d, |
|
hsScalar r, hsScalar g, hsScalar b, hsScalar a, |
|
hsBool spheric) |
|
{ |
|
|
|
hsGViewClipState* clipState = v->SaveClipDisabled(); |
|
if( hsGClip3::kClipCulled & v->ClipTestBounds(this) ) |
|
{ |
|
v->RestoreClipDisabled(clipState); |
|
return; |
|
} |
|
|
|
// Setup Material |
|
hsGMaterial *mat = TRACKED_NEW hsGMaterial; |
|
hsGLayer* lay = mat->MakeBaseLayer(); |
|
lay->SetAmbientColor(r,g,b,a); |
|
lay->SetMiscFlags(hsGMatState::kMiscWireFrame | hsGMatState::kMiscTwoSided); |
|
lay->SetShadeFlags(hsGMatState::kShadeNoShade); |
|
mat->SetLayer(lay, 0); |
|
|
|
// Setup tMesh |
|
hsGTriMesh tMesh; |
|
if( spheric ) |
|
MakeTriMeshSphere(&tMesh); |
|
else |
|
MakeTriMesh(&tMesh, hsTriangle3::kTwoSided); |
|
|
|
|
|
tMesh.SetMaterial(mat); |
|
hsRefCnt_SafeUnRef(mat); |
|
|
|
tMesh.Render(v,d); |
|
|
|
v->RestoreClipDisabled(clipState); |
|
} |
|
#endif // MESH_GEN_DEFER |
|
|
|
void hsBounds3::Transform(const hsMatrix44 *mat) |
|
{ |
|
#if 0 // IDENT |
|
if( mat->fFlags & hsMatrix44::kIsIdent ) |
|
return; |
|
#endif // IDENT |
|
|
|
hsAssert(fType != kBoundsUninitialized, "Can't transform an unitialized bound"); |
|
if(fType == kBoundsNormal) |
|
{ |
|
hsPoint3 corners[8]; |
|
this->GetCorners(corners); |
|
|
|
mat->MapPoints(8, corners); |
|
this->Reset(8,corners); |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
} |
|
|
|
void hsBounds3::Reset(const hsPoint3 *p) |
|
{ |
|
fType = kBoundsNormal; |
|
fMins = fMaxs = *p; |
|
fBounds3Flags |= kCenterValid; |
|
fCenter = *p; |
|
} |
|
|
|
void hsBounds3::Reset(const hsBounds3 *b) |
|
{ |
|
if( kBoundsNormal == b->fType ) |
|
{ |
|
fType = kBoundsNormal; |
|
fMins = b->fMins; |
|
fMaxs = b->fMaxs; |
|
if( b->fBounds3Flags & kCenterValid ) |
|
{ |
|
fBounds3Flags |= kCenterValid; |
|
fCenter = b->fCenter; |
|
} |
|
else |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
else |
|
fType = b->fType; |
|
} |
|
|
|
void hsBounds3::Reset(int n, const hsPoint3 *p) |
|
{ |
|
fType = kBoundsNormal; |
|
fMins = fMaxs = *p; |
|
for(int i = 1; i < n ; i++) |
|
this->Union(&p[i]); |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
|
|
void hsBounds3::Union(const hsPoint3 *p) |
|
{ |
|
if(fType == kBoundsNormal) // Add this point if bounds is normal |
|
{ |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
if ((*p)[i] > fMaxs[i]) |
|
fMaxs[i] =(*p)[i]; |
|
else if ((*p)[i] < fMins[i]) |
|
fMins[i] =(*p)[i]; |
|
} |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
else |
|
{ |
|
if(fType != kBoundsFull) // Otherwise re-init unless bounds is full already |
|
this->Reset(p); |
|
} |
|
} |
|
|
|
void hsBounds3::Union(const hsVector3 *v) |
|
{ |
|
if(fType == kBoundsNormal) // Add this point if bounds is normal |
|
{ |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
if( (*v)[i] > 0 ) |
|
fMaxs[i] += (*v)[i]; |
|
else |
|
fMins[i] += (*v)[i]; |
|
} |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
} |
|
|
|
|
|
void hsBounds3::Union(const hsBounds3 *p) |
|
{ |
|
if(fType == kBoundsNormal && p->GetType() == kBoundsNormal) // Add this point if bounds is normal |
|
{ |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
if (p->fMaxs[i] > fMaxs[i]) |
|
fMaxs[i] = p->fMaxs[i]; |
|
if (p->fMins[i] < fMins[i]) |
|
fMins[i] = p->fMins[i]; |
|
} |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
else if(fType == kBoundsEmpty || fType == kBoundsUninitialized) |
|
{ |
|
*this = *p; |
|
} |
|
// If fType is kBoundsFull don't do anything |
|
} |
|
|
|
void hsBounds3::MakeSymmetric(const hsPoint3* p) |
|
{ |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
hsScalar delMax = 0; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar delUp; |
|
|
|
delUp = fMaxs[i] - (*p)[i]; |
|
delMax = hsMaximum(delMax, delUp); |
|
delUp = (*p)[i] - fMins[i]; |
|
delMax = hsMaximum(delMax, delUp); |
|
} |
|
const hsScalar sqrtTwo = 1.41421f; |
|
delMax *= sqrtTwo; |
|
hsAssert((delMax > -1.e6f)&&(delMax < 1.e6f), "MakeSymmetric going out to sea"); |
|
fCenter = *p; |
|
fMaxs.Set(delMax, delMax, delMax); |
|
fMaxs += fCenter; |
|
fMins.Set(-delMax, -delMax, -delMax); |
|
fMins += fCenter; |
|
fBounds3Flags |= kCenterValid; |
|
} |
|
|
|
void hsBounds3::InscribeSphere() |
|
{ |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
const hsScalar ooSix = hsScalarInvert(2.f * 3.f); |
|
hsScalar a = GetMaxDim() * ooSix; |
|
hsPoint3 p = GetCenter(); |
|
p.fX += a; |
|
p.fY += a; |
|
p.fZ += a; |
|
fMaxs = p; |
|
a *= -2.f; |
|
p.fX += a; |
|
p.fY += a; |
|
p.fZ += a; |
|
fMins = p; |
|
|
|
// Center still valid, type still normal |
|
} |
|
|
|
// neg, pos, zero == disjoint, I contain other, overlap |
|
Int32 hsBounds3::TestBound(const hsBounds3& other) const |
|
{ |
|
Int32 retVal = 1; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( GetMins()[i] > other.GetMaxs()[i] ) |
|
return -1; |
|
if( GetMaxs()[i] < other.GetMins()[i] ) |
|
return -1; |
|
|
|
if( GetMaxs()[i] < other.GetMaxs()[i] ) |
|
retVal = 0; |
|
if( GetMins()[i] > other.GetMins()[i] ) |
|
retVal = 0; |
|
} |
|
return retVal; |
|
} |
|
|
|
hsBool hsBounds3::IsInside(const hsPoint3* pos) const |
|
{ |
|
hsAssert(fType != kBoundsUninitialized, "Invalid bounds type for hsBounds3::IsInside() "); |
|
if(fType == kBoundsEmpty) |
|
return false; |
|
if(fType == kBoundsFull) |
|
return true; |
|
return !(pos->fX>fMaxs.fX || pos->fY>fMaxs.fY || pos->fZ>fMaxs.fZ || |
|
pos->fX<fMins.fX || pos->fY<fMins.fY || pos->fZ<fMins.fZ); |
|
} |
|
|
|
#if 0 // MESH_GEN_DEFER |
|
void hsBounds3::MakeTriMeshSphere(hsGTriMesh* tMesh, hsPoint3* cornersIn) const |
|
{ |
|
hsPoint3 center = (*GetMaxs() + *GetMins()) * 0.5f; |
|
hsScalar radius = GetMaxDim() * 0.5f; |
|
|
|
const int nLong = 9; |
|
const int nLati = 5; |
|
|
|
int nPts = nLong * nLati + 3; |
|
int nFaces = nLong * 2 + nLong * (nLati - 1) * 2; // == nLong * nLati * 2 |
|
tMesh->AllocatePointers(nFaces /*faces*/, nPts /*pts*/, 0 /*uvs*/, 0 /*colors*/); |
|
tMesh->SetNumTriVertex(nPts); |
|
|
|
int iCenter = nPts - 3; |
|
int iNorthPole = nPts - 2; |
|
int iSouthPole = nPts - 1; |
|
hsPoint3 pt; |
|
pt = center; |
|
tMesh->SetPoint(iCenter, &pt); |
|
pt.fZ += radius; |
|
tMesh->SetPoint(iNorthPole, &pt); |
|
pt.fZ -= 2.f * radius; |
|
tMesh->SetPoint(iSouthPole, &pt); |
|
|
|
int i, j; |
|
for( i = 0; i < nLong; i++ ) |
|
{ |
|
for( j = 0; j < nLati; j++ ) |
|
{ |
|
hsScalar theta = (hsScalar(i) / nLong) * 2.f * hsScalarPI; |
|
hsScalar cosTheta = hsCosine(theta); |
|
hsScalar sinTheta = hsSine(theta); |
|
|
|
hsScalar phi = (hsScalar(j+1) / (nLati+1)) * hsScalarPI; |
|
hsScalar cosPhi = hsCosine(phi); |
|
hsScalar sinPhi = hsSine(phi); |
|
|
|
pt.fX = center.fX + radius * sinPhi * cosTheta; |
|
pt.fY = center.fY + radius * sinPhi * sinTheta; |
|
pt.fZ = center.fZ + radius * cosPhi; |
|
tMesh->SetPoint(j + i * nLati, &pt); |
|
} |
|
} |
|
|
|
hsTriangle3* tri; |
|
int nTris = 0; |
|
int iNext; |
|
for( i = 0; i < nLong; i++ ) |
|
{ |
|
if( (iNext = i + 1) >= nLong ) |
|
iNext = 0; |
|
|
|
tri = tMesh->GetTriFromPool(nTris); |
|
tri->Zero(); |
|
tri->fFlags |= hsTriangle3::kTwoSided; |
|
tMesh->SetTriangle(nTris++, tri); |
|
tri->SetQuickMeshVerts(i * nLati, iNext * nLati, iNorthPole); |
|
|
|
tri = tMesh->GetTriFromPool(i); |
|
tri->Zero(); |
|
tri->fFlags |= hsTriangle3::kTwoSided; |
|
tMesh->SetTriangle(nTris++, tri); |
|
tri->SetQuickMeshVerts(nLati-1 + iNext * nLati, nLati-1 + i * nLati, iSouthPole); |
|
|
|
int jNext; |
|
for( j = 0; j < nLati-1; j++ ) |
|
{ |
|
jNext = j + 1; |
|
|
|
tri = tMesh->GetTriFromPool(nTris); |
|
tri->Zero(); |
|
tri->fFlags |= hsTriangle3::kTwoSided; |
|
tMesh->SetTriangle(nTris++, tri); |
|
tri->SetQuickMeshVerts(j + i * nLati, j + iNext * nLati, jNext + i * nLati); |
|
|
|
tri = tMesh->GetTriFromPool(nTris); |
|
tri->Zero(); |
|
tri->fFlags |= hsTriangle3::kTwoSided; |
|
tMesh->SetTriangle(nTris++, tri); |
|
tri->SetQuickMeshVerts(jNext + iNext * nLati, jNext + i * nLati, j + iNext * nLati); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// Allocate and create mesh from bounding box |
|
// |
|
void hsBounds3::MakeTriMesh(hsGTriMesh* tMesh, UInt32 triFlags, hsPoint3* cornersIn) const |
|
{ |
|
hsAssert(cornersIn || fType == kBoundsNormal, |
|
"Invalid bounds type for hsBounds3::MakeTriMesh "); |
|
|
|
const int maxNew= 12; |
|
// Setup tMesh |
|
tMesh->AllocatePointers(maxNew /*faces*/, 8 /*pts*/, 0 /*uvs*/, 0 /*colors*/); |
|
tMesh->SetNumTriVertex(8); |
|
int i; |
|
hsPoint3 corners[8]; |
|
// Set Points |
|
if( !cornersIn ) |
|
{ |
|
GetCorners(corners); |
|
cornersIn = corners; |
|
} |
|
for(i=0; i<8; i++) |
|
{ |
|
tMesh->SetPoint(i, &cornersIn[i]); |
|
} |
|
tMesh->GetVertexPool()->SetCount(8); |
|
|
|
// Set faces |
|
hsTriangle3 *tri; |
|
int triNum=0; |
|
|
|
|
|
static int verts[maxNew * 3] = { |
|
/* -Y */ 6,2,3, |
|
/* -Y */ 6,3,7, |
|
/* Y */ 5,1,0, |
|
/* Y */ 5,0,4, |
|
/* -X */ 7,3,1, |
|
/* -X */ 7,1,5, |
|
/* X */ 4,0,2, |
|
/* X */ 4,2,6, |
|
/* Z */ 3,0,1, |
|
/* Z */ 3,2,0, |
|
/* -Z */ 7,4,6, |
|
/* -Z */ 7,5,4 |
|
}; |
|
int v=0; |
|
for (;triNum < maxNew;triNum++) |
|
{ |
|
tri = tMesh->GetTriFromPool(triNum); |
|
tri->Zero(); |
|
tri->fFlags |= triFlags; |
|
tMesh->SetTriangle(triNum, tri); |
|
tri->SetQuickMeshVerts(verts[v + 0],verts[v + 1],verts[v + 2]); |
|
v += 3; |
|
} |
|
tMesh->SetTrianglePointers(); |
|
} |
|
#endif // MESH_GEN_DEFER |
|
|
|
void hsBounds3::TestPlane(const hsPlane3 *p, hsPoint2 &depth) const |
|
{ |
|
TestPlane(p->fN, depth); |
|
} |
|
void hsBounds3::TestPlane(const hsVector3 &n, hsPoint2 &depth) const |
|
{ |
|
hsAssert(fType == kBoundsNormal, "TestPlane only valid for kBoundsNormal filled bounds"); |
|
|
|
hsScalar dmax = fMins.InnerProduct(n); |
|
hsScalar dmin = dmax; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar dd; |
|
dd = fMaxs[i] - fMins[i]; |
|
dd *= n[i]; |
|
|
|
if( dd < 0 ) |
|
dmin += dd; |
|
else |
|
dmax += dd; |
|
} |
|
|
|
depth.fX = dmin; |
|
depth.fY = dmax; |
|
} |
|
|
|
hsScalar hsBounds3::ClosestPointToLine(const hsPoint3 *p, const hsPoint3 *v0, const hsPoint3 *v1, hsPoint3 *out) |
|
{ |
|
hsVector3 del(v1, v0); |
|
hsScalar magSq = del.MagnitudeSquared(); |
|
hsScalar t = 0.f; |
|
if( magSq < hsBounds::kRealSmall ) |
|
{ |
|
*out = *v0; |
|
} |
|
else |
|
{ |
|
t = del.InnerProduct(hsVector3(p, v0)) * hsScalarInvert(magSq); |
|
if( t >= hsScalar1 ) |
|
*out = *v1; |
|
else if( t <= 0 ) |
|
*out = *v0; |
|
else |
|
*out = *v0 + del * t; |
|
} |
|
return t; |
|
} |
|
|
|
hsScalar hsBounds3::ClosestPointToInfiniteLine(const hsPoint3* p, const hsVector3* v, hsPoint3* out) |
|
{ |
|
hsScalar magSq = v->MagnitudeSquared(); |
|
hsScalar t = 0.f; |
|
hsPoint3 origin(0,0,0); |
|
if( magSq < hsBounds::kRealSmall ) |
|
{ |
|
*out = origin; |
|
} |
|
else |
|
{ |
|
t = v->InnerProduct(hsVector3(*p)) * hsScalarInvert(magSq); |
|
*out = hsPoint3(*v * t); |
|
} |
|
return t; |
|
} |
|
|
|
hsBool hsBounds3::ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const |
|
{ |
|
// Look for axis intervals p is within |
|
int nSect = 0; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( p[i] < fMins[i] ) |
|
{ |
|
inner[i] = fMins[i]; |
|
outer[i] = fMaxs[i]; |
|
} |
|
else if( p[i] > fMaxs[i] ) |
|
{ |
|
inner[i] = fMaxs[i]; |
|
outer[i] = fMins[i]; |
|
} |
|
else |
|
{ |
|
inner[i] = p[i]; |
|
outer[i] = (p[i] - fMins[i] > fMaxs[i] - p[i]) ? fMins[i] : fMaxs[i]; |
|
nSect++; |
|
} |
|
} |
|
|
|
return nSect == 3; |
|
} |
|
|
|
void hsBounds3::Read(hsStream *stream) |
|
{ |
|
hsBounds::Read(stream); |
|
fMins.Read(stream); |
|
fMaxs.Read(stream); |
|
fBounds3Flags = 0; |
|
} |
|
|
|
void hsBounds3::Write(hsStream *stream) |
|
{ |
|
hsBounds::Write(stream); |
|
fMins.Write(stream); |
|
fMaxs.Write(stream); |
|
} |
|
|
|
////////////////////////////////// |
|
////////////////////////////////////////////////// |
|
// Plane Bounds util class |
|
////////////////////////////////////////////////// |
|
|
|
hsPoint3 hsBoundsOriented::GetCenter() const |
|
{ |
|
hsAssert(fCenterValid==true, "Unset center for hsBoundsOriented::GetCenter()"); |
|
return fCenter; |
|
} |
|
|
|
void hsBoundsOriented::TestPlane(const hsVector3 &n, hsPoint2 &depth) const |
|
{ |
|
hsAssert(false, "TestPlane not a valid operation for hsBounsOriented"); |
|
} |
|
// |
|
// Return true if inside all the planes |
|
// |
|
hsBool hsBoundsOriented::IsInside(const hsPoint3* pos) const |
|
{ |
|
hsAssert(fType == kBoundsNormal, "Invalid bounds type for hsBounds3::IsInside() "); |
|
if(fType == kBoundsEmpty) |
|
return false; |
|
if(fType == kBoundsFull) |
|
return true; |
|
int i; |
|
for( i = 0; i < fNumPlanes; i++ ) |
|
{ |
|
hsScalar dis = fPlanes[i].fN.InnerProduct(pos); |
|
dis += fPlanes[i].fD; |
|
if( dis > 0.f ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void hsBoundsOriented::SetNumberPlanes(UInt32 n) |
|
{ |
|
delete [] fPlanes; |
|
fPlanes = TRACKED_NEW hsPlane3[fNumPlanes = n]; |
|
} |
|
|
|
void hsBoundsOriented::SetPlane(UInt32 i, hsPlane3 *pln) |
|
{ |
|
fType = kBoundsNormal; |
|
if( i >= fNumPlanes ) |
|
{ |
|
hsPlane3 *newPlanes = TRACKED_NEW hsPlane3[i+1]; |
|
if( fPlanes ) |
|
{ |
|
int k; |
|
for( k = 0; k < fNumPlanes; k++ ) |
|
*newPlanes++ = *fPlanes++; |
|
delete [] fPlanes; |
|
} |
|
fPlanes = newPlanes; |
|
fNumPlanes = i+1; |
|
} |
|
fPlanes[i] = *pln; |
|
} |
|
|
|
// |
|
// Make mesh from bounds3. Make boundsOriented from mesh tris. |
|
// |
|
void hsBoundsOriented::Reset(const hsBounds3* bounds) |
|
{ |
|
#if 0 // MESH_GEN_DEFER |
|
hsGTriMesh tMesh; |
|
bounds->MakeTriMesh(&tMesh, 0 /* triFlags */); |
|
Reset(&tMesh); |
|
#endif // MESH_GEN_DEFER |
|
} |
|
|
|
#if 0 |
|
// |
|
// Make mesh from bounds3. Make boundsOriented from mesh tris. |
|
// |
|
void hsBoundsOriented::Union(const hsBounds3 *b) |
|
{ |
|
#if 0 // MESH_GEN_DEFER |
|
hsGTriMesh tMesh; |
|
bounds->MakeTriMesh(&tMesh); |
|
int i; |
|
hsTriangle3 tri; |
|
for (i=0; i<tMesh.GetNumTriangles(); i++) |
|
{ |
|
tMesh.GetTriangle(i, &tri); |
|
Union(&tri); |
|
} |
|
#endif // MESH_GEN_DEFER |
|
} |
|
#endif |
|
|
|
// |
|
// |
|
// |
|
void hsBoundsOriented::Reset(hsGTriMesh *tMesh) |
|
{ |
|
#if 0 // MESH_GEN_DEFER |
|
const float OBJCVT_ABOUT_ZERO = 1.0e-4f; |
|
const float OBJCVT_ABOUT_ONE = 1.0f - OBJCVT_ABOUT_ZERO; |
|
|
|
hsPlane3 *planes = TRACKED_NEW hsPlane3[tMesh->GetNumTriangles()]; |
|
UInt32 nPlanes = 0; |
|
|
|
int i; |
|
for( i = 0; i < tMesh->GetNumTriangles(); i++ ) |
|
{ |
|
hsTriangle3 *tri; |
|
tri = tMesh->GetTriangle(i); |
|
hsPlane3 pln; |
|
tri->ComputePlane(&pln); |
|
|
|
hsScalar norm = hsFastMath::InvSqrRoot(pln.fN.MagnitudeSquared()); |
|
pln.fN *= norm; |
|
|
|
int j; |
|
for( j = 0; j < nPlanes; j++ ) |
|
{ |
|
if( (pln.fN.InnerProduct(planes[j].fN)> OBJCVT_ABOUT_ONE) |
|
&&((pln.fD/planes[j].fD) >= 1.0-OBJCVT_ABOUT_ZERO) |
|
&&((pln.fD/planes[j].fD) <= 1.0+OBJCVT_ABOUT_ZERO) ) |
|
break; |
|
} |
|
if( j == nPlanes ) |
|
planes[nPlanes++] = pln; |
|
} |
|
|
|
SetNumberPlanes(nPlanes); |
|
for( i = 0; i < nPlanes; i++ ) |
|
SetPlane(i, planes+i); |
|
|
|
delete [] planes; |
|
|
|
// Compute center |
|
hsPoint3 centroid(0,0,0); |
|
for(i=0; i<tMesh->GetNumPoints(); i++) |
|
{ |
|
centroid = centroid + *tMesh->GetPoint(i); |
|
} |
|
centroid = centroid / (hsScalar)tMesh->GetNumPoints(); |
|
SetCenter(¢roid); |
|
#endif // MESH_GEN_DEFER |
|
} |
|
|
|
|
|
void hsBoundsOriented::Write(hsStream *stream) |
|
{ |
|
hsBounds::Write(stream); |
|
fCenter.Write(stream); |
|
stream->WriteSwap32(fCenterValid); |
|
stream->WriteSwap32(fNumPlanes); |
|
int i; |
|
for( i = 0; i < fNumPlanes; i++ ) |
|
{ |
|
fPlanes[i].Write(stream); |
|
} |
|
} |
|
|
|
void hsBoundsOriented::Read(hsStream *stream) |
|
{ |
|
hsBounds::Read(stream); |
|
fCenter.Read(stream); |
|
fCenterValid = (hsBool)stream->ReadSwap32(); |
|
fNumPlanes = stream->ReadSwap32(); |
|
if (fPlanes) |
|
delete [] fPlanes; |
|
fPlanes = TRACKED_NEW hsPlane3[fNumPlanes]; |
|
int i; |
|
for( i = 0; i < fNumPlanes; i++ ) |
|
{ |
|
fPlanes[i].Read(stream); |
|
} |
|
} |
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
hsBounds3Ext::hsBounds3Ext(const hsBounds3 &b) |
|
{ |
|
Reset(&b); |
|
} |
|
hsBounds3Ext &hsBounds3Ext::operator=(const hsBounds3 &b) |
|
{ |
|
Reset(&b); |
|
return *this; |
|
} |
|
|
|
void hsBounds3Ext::IMakeMinsMaxs() |
|
{ |
|
hsAssert(!(fExtFlags & kAxisAligned), "Axis aligned box defined by min and max"); |
|
fMins = fMaxs = fCorner; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if(!IAxisIsZero(i) ) |
|
{ |
|
int j; |
|
for( j = 0; j < 3; j++ ) |
|
{ |
|
if( fAxes[i][j] < 0 ) |
|
fMins[j] += fAxes[i][j]; |
|
else |
|
fMaxs[j] += fAxes[i][j]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void hsBounds3Ext::IMakeDists() const |
|
{ |
|
hsAssert(!(fExtFlags & kAxisAligned), "Dists only useful for transformed BB"); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
fDists[i].fX = fCorner.InnerProduct(fAxes[i]); |
|
if( !IAxisIsZero(i) ) |
|
{ |
|
fDists[i].fY = fDists[i].fX + fAxes[i].InnerProduct(fAxes[i]); |
|
if( fDists[i].fX > fDists[i].fY ) |
|
{ |
|
hsScalar t = fDists[i].fX; |
|
fDists[i].fX = fDists[i].fY; |
|
fDists[i].fY = t; |
|
} |
|
} |
|
else |
|
fDists[i].fY = fDists[i].fX; |
|
} |
|
fExtFlags |= kDistsSet; |
|
} |
|
|
|
void hsBounds3Ext::IMakeSphere() const |
|
{ |
|
if(!(fBounds3Flags & kCenterValid) ) |
|
ICalcCenter(); |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
if( fBounds3Flags & kIsSphere ) |
|
{ |
|
fRadius = fMaxs[0] - fMins[0]; |
|
int i; |
|
for( i = 1; i < 3; i++ ) |
|
{ |
|
hsScalar dist = fMaxs[i] - fMins[i]; |
|
if( dist < fRadius ) |
|
fRadius = dist; |
|
} |
|
fRadius *= 0.5f; |
|
} |
|
else |
|
{ |
|
fRadius = hsSquareRoot(hsVector3(&fMaxs, &fCenter).MagnitudeSquared()); |
|
} |
|
} |
|
else |
|
{ |
|
if( fBounds3Flags & kIsSphere ) |
|
{ |
|
hsScalar minMagSq = fAxes[0].MagnitudeSquared(); |
|
hsScalar magSq = fAxes[1].MagnitudeSquared(); |
|
if( magSq < minMagSq ) |
|
magSq = minMagSq; |
|
magSq = fAxes[2].MagnitudeSquared(); |
|
if( magSq < minMagSq ) |
|
magSq = minMagSq; |
|
fRadius = hsSquareRoot(magSq); |
|
} |
|
else |
|
{ |
|
hsVector3 accum; |
|
accum.Set(0,0,0); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( !IAxisIsZero(i) ) |
|
accum += fAxes[i]; |
|
} |
|
fRadius = hsSquareRoot((accum * 0.5f).MagnitudeSquared()); |
|
} |
|
} |
|
fExtFlags |= kSphereSet; |
|
} |
|
|
|
void hsBounds3Ext::Reset(const hsBounds3 *b) |
|
{ |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::Reset(b); |
|
} |
|
|
|
void hsBounds3Ext::Reset(const hsPoint3 *p) |
|
{ |
|
fExtFlags = kAxisAligned | kSphereSet; |
|
hsBounds3::Reset(p); |
|
fRadius = 0; |
|
} |
|
|
|
void hsBounds3Ext::Reset(const hsBounds3Ext *b) |
|
{ |
|
hsBounds3::Reset(b); |
|
fExtFlags = b->fExtFlags; |
|
if (!(fExtFlags & kAxisAligned)) |
|
{ |
|
fCorner = b->fCorner; |
|
fAxes[0] = b->fAxes[0]; |
|
fAxes[1] = b->fAxes[1]; |
|
fAxes[2] = b->fAxes[2]; |
|
} |
|
if (fExtFlags & kDistsSet) |
|
{ |
|
fDists[0] = b->fDists[0]; |
|
fDists[1] = b->fDists[1]; |
|
fDists[2] = b->fDists[2]; |
|
} |
|
if (fExtFlags & kSphereSet) |
|
fRadius = b->fRadius; |
|
} |
|
|
|
|
|
void hsBounds3Ext::GetCorners(hsPoint3 *b) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsBounds3::GetCorners(b); |
|
} |
|
else |
|
{ |
|
int i; |
|
for( i = 0; i < 8; i++ ) |
|
{ |
|
b[i] = fCorner; |
|
if( !(i & 0x1) && !(fExtFlags & kAxisZeroZero) )b[i] += fAxes[0]; |
|
if( !(i & 0x2) && !(fExtFlags & kAxisOneZero) )b[i] += fAxes[1]; |
|
if( !(i & 0x4) && !(fExtFlags & kAxisTwoZero) )b[i] += fAxes[2]; |
|
} |
|
} |
|
} |
|
|
|
void hsBounds3Ext::GetAxes(hsVector3 *fAxis0, hsVector3 *fAxis1, hsVector3 *fAxis2) const |
|
{ |
|
if( !(fExtFlags & kAxisAligned) ) |
|
{ |
|
*fAxis0 = fAxes[0]; |
|
*fAxis1 = fAxes[1]; |
|
*fAxis2 = fAxes[2]; |
|
} |
|
else |
|
{ |
|
fAxis0->Set(fMaxs.fX - fMins.fX, 0, 0); |
|
fAxis1->Set(0, fMaxs.fY - fMins.fY, 0); |
|
fAxis2->Set(0, 0, fMaxs.fZ - fMins.fZ); |
|
} |
|
} |
|
|
|
void hsBounds3Ext::Reset(int n, const hsPoint3 *p) |
|
{ |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::Reset(n, p); |
|
} |
|
|
|
// mf horse - could union in a point preserving axes... |
|
void hsBounds3Ext::Union(const hsPoint3 *p) |
|
{ |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::Union(p); |
|
} |
|
void hsBounds3Ext::Union(const hsVector3 *v) |
|
{ |
|
#if 0 // smarter union |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::Union(v); |
|
#else // smarter union |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsBounds3::Union(v); |
|
} |
|
else |
|
{ |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar dot = fAxes[i].InnerProduct(v); |
|
dot /= fAxes[i].MagnitudeSquared(); |
|
if( dot > 0 ) |
|
{ |
|
fAxes[i] += dot * fAxes[i]; |
|
fExtFlags &= ~(1 << (20+i)); // axis not zero no more |
|
} |
|
else if( dot < 0 ) |
|
{ |
|
hsVector3 del = dot * fAxes[i]; |
|
fCorner += del; |
|
del = -del; |
|
fAxes[i] += del; |
|
fExtFlags &= ~(1 << (20+i)); // axis not zero no more |
|
} |
|
} |
|
fExtFlags &= ~(kSphereSet | kDistsSet); |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
#endif // smarter union |
|
} |
|
|
|
void hsBounds3Ext::Union(const hsBounds3 *b) |
|
{ |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::Union(b); |
|
} |
|
|
|
void hsBounds3Ext::MakeSymmetric(const hsPoint3* p) |
|
{ |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::MakeSymmetric(p); |
|
return; |
|
} |
|
|
|
// Can do this preserving axes, but may not be worth it. |
|
fExtFlags = kAxisAligned; |
|
hsBounds3::MakeSymmetric(p); |
|
} |
|
|
|
void hsBounds3Ext::InscribeSphere() |
|
{ |
|
fBounds3Flags |= kIsSphere; |
|
fExtFlags |= kAxisAligned; |
|
IMakeSphere(); |
|
return; |
|
#if 0 |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsBounds3::InscribeSphere(); |
|
return; |
|
} |
|
|
|
const hsScalar oneThird = hsScalarInvert(3.f); |
|
// hsScalar a = GetMaxDim() * hsScalarInvert(6.f); |
|
hsScalar a = GetRadius() * oneThird; |
|
hsPoint3 p = GetCenter(); |
|
p.fX += a; |
|
p.fY += a; |
|
p.fZ += a; |
|
fMaxs = p; |
|
a *= -2.f; |
|
p.fX += a; |
|
p.fY += a; |
|
p.fZ += a; |
|
fMins = p; |
|
|
|
// Center still valid, type still normal |
|
fExtFlags = kAxisAligned; |
|
#endif |
|
} |
|
|
|
void hsBounds3Ext::Transform(const hsMatrix44 *m) |
|
{ |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
fExtFlags = 0; |
|
|
|
fCorner = *m * fMins; |
|
hsVector3 v; |
|
hsScalar span; |
|
span = fMaxs.fX - fMins.fX; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisZeroZero; |
|
span = hsScalar1; |
|
} |
|
v.Set(span, 0, 0); |
|
fAxes[0] = *m * v; |
|
span = fMaxs.fY - fMins.fY; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisOneZero; |
|
span = hsScalar1; |
|
} |
|
v.Set(0, span, 0); |
|
fAxes[1] = *m * v; |
|
span = fMaxs.fZ - fMins.fZ; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisTwoZero; |
|
span = hsScalar1; |
|
} |
|
v.Set(0, 0, span); |
|
fAxes[2] = *m * v; |
|
|
|
} |
|
else |
|
{ |
|
#if 0 // IDENT |
|
if( m->fFlags & hsMatrix44::kIsIdent ) |
|
return; |
|
#endif // IDENT |
|
|
|
fCorner = *m * fCorner; |
|
fAxes[0] = *m * fAxes[0]; |
|
fAxes[1] = *m * fAxes[1]; |
|
fAxes[2] = *m * fAxes[2]; |
|
|
|
fExtFlags &= kAxisZeroZero|kAxisOneZero|kAxisTwoZero; |
|
} |
|
IMakeMinsMaxs(); |
|
fBounds3Flags &= ~kCenterValid; |
|
} |
|
|
|
void hsBounds3Ext::Translate(const hsVector3 &v) |
|
{ |
|
if( fType != kBoundsNormal ) |
|
return; |
|
|
|
fMins += v; |
|
fMaxs += v; |
|
if( fBounds3Flags & kCenterValid ) |
|
fCenter += v; |
|
if( !(fExtFlags & kAxisAligned) ) |
|
{ |
|
fCorner += v; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar d; |
|
d = fAxes[i].InnerProduct(v); |
|
fDists[i].fX += d; |
|
fDists[i].fY += d; |
|
} |
|
} |
|
} |
|
|
|
hsBool hsBounds3Ext::IsInside(const hsPoint3 *p) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
return hsBounds3::IsInside(p); |
|
|
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar diss = p->InnerProduct(fAxes[i]); |
|
if( (diss < fDists[i].fX) |
|
||(diss > fDists[i].fY) ) |
|
return false; |
|
} |
|
return true; |
|
|
|
} |
|
|
|
// neg, pos, zero == disjoint, I contain other, overlap |
|
Int32 hsBounds3Ext::TestBound(const hsBounds3Ext& other) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
return hsBounds3::TestBound(other); |
|
|
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
|
|
Int32 retVal = 1; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsPoint2 depth; |
|
other.TestPlane(fAxes[i], depth); |
|
|
|
if( fDists[i].fX > depth.fY ) |
|
return -1; |
|
if( fDists[i].fY < depth.fX ) |
|
return -1; |
|
|
|
if( fDists[i].fY < depth.fY ) |
|
retVal = 0; |
|
if( fDists[i].fX > depth.fX ) |
|
retVal = 0; |
|
} |
|
return retVal; |
|
} |
|
|
|
|
|
void hsBounds3Ext::TestPlane(const hsVector3 &n, hsPoint2 &depth) const |
|
{ |
|
hsAssert(fType == kBoundsNormal, "TestPlane only valid for kBoundsNormal filled bounds"); |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsBounds3::TestPlane(n, depth); |
|
} |
|
else |
|
{ |
|
hsScalar dmax = fCorner.InnerProduct(n); |
|
hsScalar dmin = dmax; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( !IAxisIsZero(i) ) |
|
{ |
|
hsScalar d; |
|
d = fAxes[i].InnerProduct(n); |
|
if( d < 0 ) |
|
dmin += d; |
|
else |
|
dmax += d; |
|
} |
|
} |
|
|
|
depth.fX = dmin; |
|
depth.fY = dmax; |
|
} |
|
} |
|
|
|
void hsBounds3Ext::TestPlane(const hsPlane3 *p, const hsVector3 &myVel, hsPoint2 &depth) const |
|
{ |
|
TestPlane(p->fN, myVel, depth); |
|
} |
|
void hsBounds3Ext::TestPlane(const hsVector3 &n, const hsVector3 &myVel, hsPoint2 &depth) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsScalar dmax = fMins.InnerProduct(n); |
|
hsScalar dmin = dmax; |
|
hsScalar dvel = myVel.InnerProduct(n); |
|
if( dvel < 0 ) |
|
dmin += dvel; |
|
else |
|
dmax += dvel; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar dd; |
|
dd = fMaxs[i] - fMins[i]; |
|
dd *= n[i]; |
|
|
|
if( dd < 0 ) |
|
dmin += dd; |
|
else |
|
dmax += dd; |
|
} |
|
|
|
depth.fX = dmin; |
|
depth.fY = dmax; |
|
} |
|
else |
|
{ |
|
hsScalar dmax = fCorner.InnerProduct(n); |
|
hsScalar dmin = dmax; |
|
hsScalar dvel = myVel.InnerProduct(n); |
|
if( dvel < 0 ) |
|
dmin += dvel; |
|
else |
|
dmax += dvel; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( !IAxisIsZero(i) ) |
|
{ |
|
hsScalar d; |
|
d = fAxes[i].InnerProduct(n); |
|
if( d < 0 ) |
|
dmin += d; |
|
else |
|
dmax += d; |
|
} |
|
} |
|
|
|
depth.fX = dmin; |
|
depth.fY = dmax; |
|
} |
|
} |
|
|
|
Int32 hsBounds3Ext::TestPoints(int n, const hsPoint3 *pList, const hsVector3 &ptVel) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
Int32 retVal = -1; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar effMax = fMaxs[i]; |
|
hsScalar effMin = fMins[i]; |
|
if( ptVel[i] < 0 ) |
|
effMax -= ptVel[i]; |
|
else |
|
effMin -= ptVel[i]; |
|
|
|
int j; |
|
const UInt32 low = 0x1, hi = 0x2; |
|
UInt32 mask = low | hi; |
|
for( j = 0; j < n; j++ ) |
|
{ |
|
if( pList[j][i] > effMin ) |
|
mask &= ~low; |
|
if( pList[j][i] < effMax ) |
|
mask &= ~hi; |
|
if( mask ) |
|
retVal = 0; |
|
} |
|
if( mask ) |
|
return 1; |
|
} |
|
return retVal; |
|
} |
|
else // non-axis aligned case |
|
{ |
|
Int32 retVal = -1; // all inside |
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar diff = fAxes[i].InnerProduct(ptVel); |
|
hsBool someLow = false; |
|
hsBool someHi = false; |
|
hsBool someIn = false; |
|
int j; |
|
for( j = 0; j < n; j++ ) |
|
{ |
|
hsScalar d = fAxes[i].InnerProduct(pList[j]); |
|
hsScalar ddiff = d + diff; |
|
if( d < fDists[i].fX ) |
|
someLow = true; |
|
else if( d > fDists[i].fY ) |
|
someHi = true; |
|
else |
|
someIn = true; |
|
|
|
if( ddiff < fDists[i].fX ) |
|
someLow = true; |
|
else if( ddiff > fDists[i].fY ) |
|
someHi = true; |
|
else |
|
someIn = true; |
|
|
|
if( someIn &&(someHi || someLow) ) |
|
break; |
|
} |
|
if( someHi && !(someLow || someIn) ) |
|
return 1; |
|
if( someLow && !(someHi || someIn) ) |
|
return 1; |
|
if( someHi || someLow ) |
|
retVal = 0; |
|
} |
|
return retVal; |
|
} |
|
} |
|
|
|
Int32 hsBounds3Ext::TestPoints(int n, const hsPoint3 *pList) const |
|
{ |
|
hsBool someIn = false; |
|
hsBool someOut = false; |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
{ |
|
if( IsInside(pList+i) ) |
|
someIn = true; |
|
else |
|
someOut = true; |
|
if( someIn && someOut ) |
|
return 0; |
|
} |
|
if( someIn ) |
|
return -1; |
|
return 1; |
|
} |
|
|
|
hsBool hsBounds3Ext::ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
return hsBounds3::ClosestPoint(p, inner, outer); |
|
|
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
|
|
int nSect = 0; |
|
inner = outer = fCorner; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar dist = fAxes[i].InnerProduct(p); |
|
if( dist < fDists[i].fX ) |
|
{ |
|
outer += fAxes[i]; |
|
} |
|
else if( dist > fDists[i].fY ) |
|
{ |
|
inner += fAxes[i]; |
|
} |
|
else |
|
{ |
|
hsScalar t = (dist - fDists[i].fX) / (fDists[i].fY - fDists[i].fX); |
|
inner += t * fAxes[i]; |
|
if( t > 0.5f ) |
|
outer += fAxes[i]; |
|
|
|
nSect++; |
|
} |
|
} |
|
return nSect == 3; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
if( other.fExtFlags & kAxisAligned ) |
|
return ISectABB(other, myVel); |
|
|
|
return other.ISectBB(*this, -myVel); |
|
} |
|
hsAssert(!(fExtFlags & kAxisAligned), "Other can be axis-aligned, but not me!"); |
|
hsPoint2 depth; |
|
|
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
if( !(other.fExtFlags & (kDistsSet|kAxisAligned)) ) |
|
other.IMakeDists(); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
other.TestPlane(fAxes[i], -myVel, depth); |
|
if( (depth.fX > fDists[i].fY) |
|
||(depth.fY < fDists[i].fX) ) |
|
return false; |
|
|
|
if( other.fExtFlags & kAxisAligned ) |
|
{ |
|
hsScalar myMin = fMins[i]; |
|
hsScalar myMax = fMaxs[i]; |
|
if( myVel[i] < 0 ) |
|
myMin += myVel[i]; |
|
else |
|
myMax += myVel[i]; |
|
if( (other.fMins[i] > myMax) |
|
||(other.fMaxs[i] < myMin) ) |
|
return false; |
|
} |
|
else |
|
{ |
|
TestPlane(other.fAxes[i], myVel, depth); |
|
if( (depth.fX > other.fDists[i].fY) |
|
||(depth.fY < other.fDists[i].fX) ) |
|
return false; |
|
} |
|
|
|
// still leaves the 3 axes of origAxis.Cross(myVel) |
|
hsVector3 ax = fAxes[i] % myVel; |
|
hsScalar dmax = fCorner.InnerProduct(ax); |
|
hsScalar dmin = dmax; |
|
int j = i+1; |
|
if( 3 == j )j = 0; |
|
hsScalar d; |
|
d = fAxes[j].InnerProduct(ax); |
|
if( d < 0 ) |
|
dmin += d; |
|
else |
|
dmax += d; |
|
j = ( j == 2 ? 0 : j+1 ); |
|
d = fAxes[j].InnerProduct(ax); |
|
if( d < 0 ) |
|
dmin += d; |
|
else |
|
dmax += d; |
|
other.TestPlane(ax, depth); |
|
if( (depth.fX > dmax) |
|
||(depth.fY < dmin) ) |
|
return false; |
|
} |
|
|
|
|
|
return true; |
|
} |
|
|
|
|
|
static hsBool ISectInterval(const hsPoint2& other, const hsPoint2& mine) |
|
{ |
|
if( other.fY - mine.fX <= 0 ) |
|
return false; |
|
if( mine.fY - other.fX <= 0 ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
static hsBool ITestDepth(const hsPoint2& other, const hsPoint2& mine, |
|
const hsVector3& inAx, |
|
hsVector3 &outAx, hsScalar& depth) |
|
{ |
|
depth = 0; |
|
hsScalar d0, d1; |
|
d0 = other.fY - mine.fX; |
|
if( d0 <= 0 ) |
|
return false; |
|
d1 = mine.fY - other.fX; |
|
if( d1 <= 0 ) |
|
return false; |
|
|
|
// if one interval is proper subset of other, skip |
|
if( (mine.fX < other.fX)^(mine.fY < other.fY) ) |
|
{ |
|
depth = 0; |
|
return true; |
|
} |
|
if( d0 < d1 ) |
|
{ |
|
outAx = inAx; |
|
depth = d0; |
|
return true; |
|
} |
|
|
|
outAx = -inAx; |
|
depth = d1; |
|
return true; |
|
} |
|
|
|
Int32 hsBounds3Ext::IClosestISect(const hsBounds3Ext& other, const hsVector3& myVel, |
|
hsScalar* tClose, hsScalar* tImpact) const |
|
{ |
|
// Should assert both have their spheres set. |
|
|
|
hsVector3 meToOt(&other.GetCenter(), &GetCenter()); |
|
|
|
// cTerm = (myCenter - otCenter)^2 - (myRad + otRad)^2 |
|
hsScalar cTerm; |
|
|
|
cTerm = GetRadius() + other.GetRadius(); |
|
cTerm *= -cTerm; |
|
|
|
hsScalar meToOtLen = meToOt.MagnitudeSquared(); |
|
cTerm += meToOtLen; |
|
if( cTerm <= 0 ) |
|
{ |
|
*tClose = *tImpact = 0; |
|
return -1; // started off in contact |
|
} |
|
|
|
hsScalar ooATerm = myVel.InnerProduct(myVel); |
|
if( ooATerm < hsBounds::kRealSmall ) |
|
{ |
|
*tClose = *tImpact = 0; |
|
return 0; |
|
} |
|
ooATerm = hsScalarInvert(ooATerm); |
|
|
|
hsScalar bTerm = myVel.InnerProduct(meToOt); |
|
bTerm *= ooATerm; |
|
hsScalar bSqTerm = bTerm * bTerm; |
|
// bTerm is t for closest point to line |
|
|
|
hsScalar det = bSqTerm - ooATerm * cTerm; |
|
if( det < 0 ) |
|
{ |
|
*tClose = *tImpact = bTerm; |
|
return 0; |
|
} |
|
|
|
det = hsSquareRoot(det); |
|
*tClose = bTerm; |
|
*tImpact = bTerm - det; |
|
|
|
return 1; |
|
} |
|
|
|
void hsBounds3Ext::Unalign() |
|
{ |
|
fExtFlags = 0; |
|
|
|
fCorner = fMins; |
|
hsVector3 v; |
|
hsScalar span; |
|
span = fMaxs.fX - fMins.fX; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisZeroZero; |
|
span = hsScalar1; |
|
} |
|
fAxes[0].Set(span, 0, 0); |
|
span = fMaxs.fY - fMins.fY; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisOneZero; |
|
span = hsScalar1; |
|
} |
|
fAxes[1].Set(0, span, 0); |
|
span = fMaxs.fZ - fMins.fZ; |
|
if( span < kRealSmall ) |
|
{ |
|
fExtFlags |= kAxisTwoZero; |
|
span = hsScalar1; |
|
} |
|
fAxes[2].Set(0, 0, span); |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const |
|
{ |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
hsBounds3Ext meUnalign(*this); |
|
meUnalign.Unalign(); |
|
return meUnalign.ISectBB(other, myVel, hit); |
|
} |
|
hsAssert(!(fExtFlags & kAxisAligned), "Other can be axis-aligned, but not me!"); |
|
hsPoint2 depth; |
|
|
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
if( !(other.fExtFlags & (kDistsSet|kAxisAligned)) ) |
|
other.IMakeDists(); |
|
|
|
const hsScalar kRealBig = 1.e30f; |
|
hsScalar tstDepths[9]; |
|
hsVector3 tstAxes[9]; |
|
hsScalar totDepth = 0; |
|
int nDeep = 0; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
const hsScalar kFavorConstant = 0.01f; // smaller is favored |
|
|
|
other.TestPlane(fAxes[i], -myVel, depth); |
|
|
|
if( !ITestDepth(depth, fDists[i], fAxes[i], tstAxes[i], tstDepths[i]) ) |
|
return false; |
|
|
|
other.TestPlane(fAxes[i], depth); |
|
if( !ISectInterval(depth, fDists[i]) ) |
|
tstDepths[i] *= kFavorConstant; |
|
|
|
if( tstDepths[i] > 0 ) |
|
{ |
|
totDepth += tstDepths[i]; |
|
nDeep++; |
|
} |
|
|
|
if( other.fExtFlags & kAxisAligned ) |
|
{ |
|
hsPoint2 mine; |
|
mine.fX = fMins[i]; |
|
mine.fY = fMaxs[i]; |
|
if( myVel[i] > 0 )mine.fY += myVel[i]; |
|
else mine.fX += myVel[i]; |
|
depth.fX = other.fMins[i]; |
|
depth.fY = other.fMaxs[i]; |
|
|
|
hsVector3 ax; |
|
ax.Set( 0 == i ? hsScalar1 : 0, |
|
1 == i ? hsScalar1 : 0, |
|
2 == i ? hsScalar1 : 0); |
|
|
|
if( !ITestDepth(depth, mine, ax, tstAxes[i+3], tstDepths[i+3]) ) |
|
return false; |
|
|
|
mine.fX = fMins[i]; |
|
mine.fY = fMaxs[i]; |
|
if( !ISectInterval(depth, mine) ) |
|
tstDepths[i+3] *= kFavorConstant; |
|
|
|
if( tstDepths[i+3] ) |
|
{ |
|
totDepth += tstDepths[i+3]; |
|
nDeep++; |
|
} |
|
} |
|
else |
|
{ |
|
TestPlane(other.fAxes[i], myVel, depth); |
|
|
|
if( !ITestDepth(other.fDists[i], depth, other.fAxes[i], tstAxes[i+3], tstDepths[i+3]) ) |
|
return false; |
|
|
|
TestPlane(other.fAxes[i], depth); |
|
|
|
if( !ISectInterval(other.fDists[i], depth) ) |
|
tstDepths[i+3] *= kFavorConstant; |
|
|
|
if( tstDepths[i+3] ) |
|
{ |
|
totDepth += tstDepths[i+3]; |
|
nDeep++; |
|
} |
|
} |
|
|
|
#if 0 |
|
// still leaves the 3 axes of origAxis.Cross(myVel) |
|
hsVector3 ax = fAxes[i] % myVel; |
|
if( ax.MagnitudeSquared() > kRealSmall ) |
|
{ |
|
hsPoint2 myDepth; |
|
myDepth.fX = myDepth.fY = fCorner.InnerProduct(ax); |
|
hsScalar d; |
|
int j = i == 2 ? 0 : i+1; |
|
if( !IAxisIsZero(j) ) |
|
{ |
|
d = fAxes[j].InnerProduct(ax); |
|
if( d < 0 ) |
|
myDepth.fX += d; |
|
else |
|
myDepth.fY += d; |
|
} |
|
j = ( j == 2 ? 0 : j+1 ); |
|
if( !IAxisIsZero(j) ) |
|
{ |
|
d = fAxes[j].InnerProduct(ax); |
|
if( d < 0 ) |
|
myDepth.fX += d; |
|
else |
|
myDepth.fY += d; |
|
} |
|
other.TestPlane(ax, depth); |
|
|
|
if( !ITestDepth(depth, myDepth, ax, tstAxes[i+6], tstDepths[i+6]) ) |
|
return false; |
|
totDepth += tstDepths[i+6]; |
|
} |
|
else |
|
tstDepths[i+6] = 0; |
|
#endif; |
|
} |
|
|
|
hsVector3 norm; |
|
if( totDepth <= 0 ) |
|
{ |
|
hsScalar t, tIgnore; |
|
IClosestISect(other, myVel, &tIgnore, &t); |
|
if( t < 0 ) |
|
t = 0; |
|
else if( t > 1.f ) |
|
t = 1.f; |
|
hsPoint3 hitPt = GetCenter() + myVel * t; |
|
norm.Set(&hitPt, &other.GetCenter()); |
|
} |
|
else |
|
{ |
|
// now do a weighted average of the axes |
|
hsAssert(totDepth > 0, "nobody home"); |
|
norm.Set(0,0,0); |
|
for( i =0; i < 6; i++ ) |
|
{ |
|
if( tstDepths[i] > 0 ) |
|
norm += tstAxes[i] / tstDepths[i]; |
|
// norm += tstAxes[i] * (1.f - tstDepths[i] / totDepth); |
|
} |
|
} |
|
hsPoint2 otherDepth; |
|
norm.Normalize(); |
|
other.TestPlane(norm, otherDepth); |
|
TestPlane(norm, myVel, depth); |
|
|
|
hit->Set(this, &other, norm, otherDepth.fY - depth.fX); |
|
|
|
// mf horse hack test |
|
if( hit->fDepth < 0 ) |
|
return false; |
|
hsAssert(hit->fDepth >= 0, "Negative Depth"); |
|
|
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectABB(const hsBounds3Ext &other, const hsVector3 &myVel) const |
|
{ |
|
hsPoint3 effMaxs = fMaxs; |
|
hsPoint3 effMins = fMins; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar effMax = fMaxs[i]; |
|
hsScalar effMin = fMins[i]; |
|
if( myVel[i] > 0 ) |
|
effMax += myVel[i]; |
|
else |
|
effMin += myVel[i]; |
|
|
|
if( (effMax < other.fMins[i]) |
|
||(effMin > other.fMaxs[i]) ) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBS(const hsBounds3Ext &other, const hsVector3 &myVel) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
if( !(other.fExtFlags & kSphereSet) ) |
|
other.IMakeSphere(); |
|
|
|
hsPoint3 closestPt = GetCenter(); |
|
// we should know whether we have a useful velocity or not... |
|
// having the speed cached away would get rid of several |
|
// such uglies... |
|
if( myVel.MagnitudeSquared() > 0 ) |
|
{ |
|
hsScalar parm = hsVector3(&other.GetCenter(), &fCenter).InnerProduct(myVel) |
|
/ myVel.InnerProduct(myVel); |
|
if( parm > 0 ) |
|
{ |
|
if( parm > hsScalar1 ) |
|
parm = hsScalar1; |
|
closestPt += myVel * parm; |
|
} |
|
} |
|
|
|
hsScalar combRad = fRadius + other.fRadius; |
|
|
|
return hsVector3(&closestPt, &other.GetCenter()).MagnitudeSquared() < combRad*combRad; |
|
} |
|
|
|
#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration |
|
hsBool hsBounds3Ext::ISectTriABB(hsBounds3Tri &tri, const hsVector3 &myVel) const |
|
{ |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar effMax = fMaxs[i]; |
|
hsScalar effMin = fMins[i]; |
|
if( myVel[i] < 0 ) |
|
effMin += myVel[i]; |
|
else |
|
effMax += myVel[i]; |
|
|
|
int j; |
|
const UInt32 low = 0x1, hi = 0x2; |
|
UInt32 mask = low | hi; |
|
for( j = 0; j < 3; j++ ) |
|
{ |
|
if( tri.fVerts[j][i] > effMin ) |
|
mask &= ~low; |
|
if( tri.fVerts[j][i] < effMax ) |
|
mask &= ~hi; |
|
} |
|
if( mask ) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::TriBSHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const |
|
{ |
|
hsPoint3 myPt = GetCenter(); |
|
myPt += myVel; |
|
|
|
hsPoint3 closePt; |
|
hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); |
|
|
|
hsVector3 repel; |
|
repel.Set(&myPt, &closePt); |
|
|
|
hsScalar myDepth; |
|
hsScalar repelMagSq = repel.MagnitudeSquared(); |
|
if( repelMagSq < hsBounds::kRealSmall ) |
|
{ |
|
repel = tri.fNormal; |
|
myDepth = GetRadius(); |
|
} |
|
else |
|
{ |
|
myDepth = hsFastMath::InvSqrt(repelMagSq); |
|
repel *= myDepth; |
|
myDepth = 1.f / myDepth; |
|
myDepth = GetRadius() - myDepth; |
|
if( myDepth < 0 ) |
|
myDepth = 0; |
|
} |
|
|
|
if( tri.fNormal.InnerProduct(myPt) < tri.fDist ) |
|
{ |
|
repel += tri.fNormal * (-2.f * repel.InnerProduct(tri.fNormal)); |
|
myDepth = GetRadius() * 2.f - myDepth; |
|
if( myDepth < 0 ) |
|
myDepth = 0; |
|
} |
|
|
|
hit->Set(this, &tri, &repel, myDepth); |
|
|
|
return true; |
|
} |
|
|
|
#if 0 // TOCENTER |
|
hsBool hsBounds3Ext::TriBBHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const |
|
{ |
|
// Find our closest point (after movement) |
|
hsPoint3 myPt = fCorner; |
|
myPt += myVel; |
|
|
|
const hsScalar kMinDist = 1.f; // Huge min dist because world is really big right now. mf horse |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar axDot = fAxes[i].InnerProduct(tri.fNormal); |
|
if( axDot < -kMinDist ) |
|
{ |
|
// moving towards |
|
myPt += fAxes[i]; |
|
} |
|
else if( axDot < kMinDist ) |
|
{ |
|
// need to interp |
|
axDot /= -(kMinDist*2.f); |
|
axDot += 0.5f; |
|
myPt += fAxes[i] * axDot; |
|
} |
|
// else moving away, skip it |
|
} |
|
|
|
// Find closest point on tri to our closest corner |
|
hsPoint3 closePt; |
|
hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); |
|
|
|
// Repel vector is from closest corner to closest point on tri |
|
hsVector3 repel; |
|
repel.Set(&myPt, &closePt); |
|
repel += (-2.f * repel.InnerProduct(tri.fNormal)) * tri.fNormal; |
|
|
|
hsScalar repelMag = hsFastMath::InvSqrt(repel.MagnitudeSquared()); |
|
|
|
if( repelMag < hsBounds::kRealSmall ) |
|
{ |
|
hsPoint2 faceDepth; |
|
TestPlane(tri.fNormal, myVel, faceDepth); |
|
hit->Set(this, &tri, &tri.fNormal, tri.fDist - faceDepth.fX); |
|
return true; |
|
} |
|
|
|
repel *= repelMag; |
|
repelMag = 1.f / repelMag; |
|
|
|
hit->Set(this, &tri, &repel, repelMag); |
|
|
|
// Return true of our closest corner projects on to tri (along normal or myVel?) |
|
return onTri; |
|
} |
|
#else // TOCENTER |
|
|
|
hsBool hsBounds3Ext::TriBBHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const |
|
{ |
|
hsPoint3 myPt = GetCenter(); |
|
myPt += myVel; |
|
|
|
hsPoint3 closePt; |
|
hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); |
|
|
|
hsVector3 repel; |
|
repel.Set(&myPt, &closePt); |
|
hsScalar repelDotNorm = repel.InnerProduct(tri.fNormal); |
|
if( repelDotNorm < 0 ) |
|
{ |
|
repel += (-2.f * repelDotNorm) * tri.fNormal; |
|
} |
|
|
|
hsScalar repelMagSq = repel.MagnitudeSquared(); |
|
if( repelMagSq < hsBounds::kRealSmall ) |
|
repel = tri.fNormal; |
|
else |
|
{ |
|
hsScalar repelMag = hsFastMath::InvSqrt(repelMagSq); |
|
repel *= repelMag; |
|
} |
|
|
|
|
|
hsPoint2 triDepth; |
|
tri.TestPlane(repel, triDepth); |
|
|
|
hsPoint2 myDepth; |
|
TestPlane(repel, myVel, myDepth); |
|
|
|
hit->Set(this, &tri, &repel, triDepth.fY - myDepth.fX); |
|
|
|
return true; |
|
} |
|
|
|
#endif // TOCENTER |
|
|
|
hsBool hsBounds3Ext::ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel) const |
|
{ |
|
hsPoint2 faceDepth; |
|
// first test box against the triangle plane |
|
TestPlane(tri.fNormal, myVel, faceDepth); |
|
|
|
if( (tri.fDist > faceDepth.fY) |
|
||(tri.fDist < faceDepth.fX) ) |
|
return false; |
|
|
|
// now test tri against box planes |
|
if( TestPoints(3, tri.fVerts, -myVel) > 0 ) |
|
return false; |
|
|
|
if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) |
|
tri.SetAxes(); |
|
|
|
hsScalar depth = tri.fDist - faceDepth.fX; |
|
hsVector3 norm = tri.fNormal; |
|
|
|
// that only leaves the planes of triEdge.Cross(vel) |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsPoint2 depths; |
|
TestPlane(tri.fPerpAxes[i], myVel, depths); |
|
if( (tri.fPerpDists[i].fY < depths.fX) |
|
||(tri.fPerpDists[i].fX > depths.fY) ) |
|
return false; |
|
|
|
#if 0 |
|
hsScalar testDepth = tri.fPerpDists[i].fY - depths.fX; |
|
if( testDepth < depth ) |
|
{ |
|
depth = testDepth; |
|
norm = tri.fPerpAxes[i]; |
|
} |
|
#endif |
|
} |
|
hsScalar vDotN = myVel.InnerProduct(tri.fNormal); |
|
if( vDotN > 0 ) |
|
depth -= vDotN; |
|
|
|
if( depth <= 0 ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const |
|
{ |
|
hsPoint2 faceDepth; |
|
// first test box against the triangle plane |
|
TestPlane(tri.fNormal, myVel, faceDepth); |
|
|
|
if( (tri.fDist > faceDepth.fY) |
|
||(tri.fDist < faceDepth.fX) ) |
|
return false; |
|
|
|
hsScalar centDist = tri.fNormal.InnerProduct(hit->fRootCenter); |
|
if( centDist < tri.fDist ) |
|
return false; |
|
|
|
// now test tri against box planes |
|
if( TestPoints(3, tri.fVerts, -myVel) > 0 ) |
|
return false; |
|
|
|
if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) |
|
tri.SetAxes(); |
|
|
|
hsScalar depth = tri.fDist - faceDepth.fX; |
|
hsVector3 norm = tri.fNormal; |
|
|
|
// that only leaves the planes of triEdge.Cross(vel) |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsPoint2 depths; |
|
TestPlane(tri.fPerpAxes[i], myVel, depths); |
|
if( (tri.fPerpDists[i].fY < depths.fX) |
|
||(tri.fPerpDists[i].fX > depths.fY) ) |
|
return false; |
|
|
|
#if 0 |
|
hsScalar testDepth = tri.fPerpDists[i].fY - depths.fX; |
|
if( testDepth < depth ) |
|
{ |
|
depth = testDepth; |
|
norm = tri.fPerpAxes[i]; |
|
} |
|
#endif |
|
} |
|
hsScalar vDotN = myVel.InnerProduct(tri.fNormal); |
|
if( vDotN > 0 ) |
|
depth -= vDotN; |
|
|
|
if( (tri.fTriFlags & hsBounds3Tri::kDoubleSide) ) |
|
{ |
|
if( tri.fNormal.InnerProduct(hit->fRootCenter) - tri.fDist < 0 ) |
|
{ |
|
depth = -tri.fDist + faceDepth.fY; |
|
if( vDotN < 0 ) |
|
depth += vDotN; |
|
|
|
tri.fNormal = -tri.fNormal; |
|
tri.fDist = -tri.fDist; |
|
} |
|
} |
|
if( depth <= 0 ) |
|
return false; |
|
|
|
// printf("ATTRIBUTE triBnd addr %x\n",&tri.fNormal); /* Takashi Nakata TEST Add */ |
|
hit->Set(this, &tri, &norm, depth); |
|
|
|
return hit->fDepth > hsBounds::kRealSmall; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
|
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); |
|
hsScalar radScaled = fRadius * tri.fNormal.Magnitude(); |
|
hsScalar centerDist = tri.fNormal.InnerProduct(fCenter); |
|
hsScalar velDist = tri.fNormal.InnerProduct(myVel); |
|
hsScalar effMin = centerDist; |
|
hsScalar effMax = centerDist; |
|
|
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
effMax += radScaled; |
|
effMin -= radScaled; |
|
|
|
if( tri.fDist <= effMin ) |
|
return false; |
|
if( tri.fDist >= effMax ) |
|
return false; |
|
|
|
// mf horse |
|
hsScalar normDepth = tri.fDist - (centerDist - radScaled + velDist); |
|
if( normDepth <= 0 ) |
|
{ |
|
// we'll report a depth of zero to (hopefully) neutralize any effects |
|
if( tri.fTriFlags & hsBounds3Tri::kDoubleSide ) |
|
{ |
|
normDepth = -tri.fDist + (centerDist + radScaled + velDist); |
|
if( normDepth > 0 ) |
|
{ |
|
tri.fDist = -tri.fDist; |
|
tri.fNormal = -tri.fNormal; |
|
} |
|
else |
|
normDepth = 0; |
|
} |
|
else |
|
normDepth = 0; |
|
} |
|
hsAssert(normDepth >= 0, "NegativeDepth"); |
|
|
|
if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) |
|
tri.SetAxes(); |
|
|
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
centerDist = tri.fPerpAxes[i].InnerProduct(fCenter); |
|
velDist = tri.fPerpAxes[i].InnerProduct(myVel); |
|
effMin = centerDist; |
|
effMax = centerDist; |
|
|
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
|
|
hsScalar radScale = fRadius * tri.fPerpAxes[i].Magnitude(); |
|
effMax += radScale; |
|
effMin -= radScale; |
|
if( tri.fPerpDists[i].fY <= effMin ) |
|
return false; |
|
if( tri.fPerpDists[i].fX >= effMax ) |
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
|
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); |
|
hsScalar radScaled = fRadius * tri.fNormal.Magnitude(); |
|
hsScalar centerDist = tri.fNormal.InnerProduct(fCenter); |
|
hsScalar velDist = tri.fNormal.InnerProduct(myVel); |
|
hsScalar effMin = centerDist; |
|
hsScalar effMax = centerDist; |
|
|
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
effMax += radScaled; |
|
effMin -= radScaled; |
|
|
|
if( tri.fDist <= effMin ) |
|
return false; |
|
if( tri.fDist >= effMax ) |
|
return false; |
|
|
|
// mf horse |
|
hsScalar normDepth = tri.fDist - (centerDist - radScaled + velDist); |
|
if( normDepth <= 0 ) |
|
{ |
|
#if 0 // need to report the collision even if the object is leaving the tri |
|
// we'll report a depth of zero to (hopefully) neutralize any effects |
|
if(!(tri.fTriFlags & hsBounds3Tri::kDoubleSide) ) |
|
return false; |
|
normDepth = -tri.fDist + (centerDist + radScaled + velDist); |
|
if( normDepth <= 0 ) |
|
return false; |
|
tri.fDist = -tri.fDist; |
|
tri.fNormal = -tri.fNormal; |
|
#else |
|
// we'll report a depth of zero to (hopefully) neutralize any effects |
|
if( tri.fTriFlags & hsBounds3Tri::kDoubleSide ) |
|
{ |
|
normDepth = -tri.fDist + (centerDist + radScaled + velDist); |
|
if( normDepth > 0 ) |
|
{ |
|
tri.fDist = -tri.fDist; |
|
tri.fNormal = -tri.fNormal; |
|
} |
|
else |
|
normDepth = 0; |
|
} |
|
else |
|
normDepth = 0; |
|
#endif |
|
} |
|
hsAssert(normDepth >= 0, "NegativeDepth"); |
|
|
|
if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) |
|
tri.SetAxes(); |
|
|
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
centerDist = tri.fPerpAxes[i].InnerProduct(fCenter); |
|
velDist = tri.fPerpAxes[i].InnerProduct(myVel); |
|
effMin = centerDist; |
|
effMax = centerDist; |
|
|
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
|
|
hsScalar radScale = fRadius * tri.fPerpAxes[i].Magnitude(); |
|
effMax += radScale; |
|
effMin -= radScale; |
|
if( tri.fPerpDists[i].fY <= effMin ) |
|
return false; |
|
if( tri.fPerpDists[i].fX >= effMax ) |
|
return false; |
|
|
|
} |
|
|
|
hsScalar invLen = hsScalarInvert(tri.fNormal.Magnitude()); |
|
hit->Set(this, &tri, &tri.fNormal, normDepth); |
|
|
|
// mf horse - move this into Set()? |
|
hit->fNormal *= invLen; |
|
hit->fDepth *= invLen; |
|
|
|
return true; |
|
} |
|
|
|
#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration |
|
|
|
hsBool hsBounds3Ext::ISectBSBS(const hsBounds3Ext& other, const hsVector3& myVel, hsHitInfoExt *hit) const |
|
{ |
|
if(!(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
if(!(other.fExtFlags & kSphereSet) ) |
|
other.IMakeSphere(); |
|
|
|
hsScalar tClose, tImpact; |
|
if( !IClosestISect(other, myVel, &tClose, &tImpact) ) |
|
return false; |
|
if( (tImpact < 0) || (tImpact > 1.f) ) |
|
return false; |
|
if( tClose < 0 ) |
|
tClose = 0; |
|
if( tClose > 1.f ) |
|
tClose = 1.f; |
|
|
|
hsPoint3 closePt = GetCenter() + myVel * tClose; |
|
hsVector3 del; |
|
del.Set(&closePt, &other.GetCenter()); |
|
|
|
hsScalar mag = del.Magnitude(); |
|
hsScalar depth = GetRadius() + other.GetRadius() - mag; |
|
if( depth <= 0 ) |
|
return false; |
|
|
|
hsPoint3 hitPt = GetCenter() + myVel * tImpact; |
|
hsVector3 norm; |
|
norm.Set(&hitPt, &other.GetCenter()); |
|
norm.Normalize(); |
|
|
|
hit->Set(this, &other, norm, depth); |
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBSBox(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const |
|
{ |
|
hit->fDelPos = -myVel; |
|
if( other.ISectBoxBS(*this, hit->fDelPos, hit) ) |
|
{ |
|
hit->fNormal = -hit->fNormal; |
|
hit->fBoxBnd = this; |
|
hit->fOtherBoxBnd = &other; |
|
hit->fDelPos = myVel; |
|
|
|
return true; |
|
} |
|
|
|
hit->fDelPos = myVel; |
|
return false; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const |
|
{ |
|
if(!(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (BoxBS(vel))"); |
|
|
|
hsVector3 minAxis; |
|
hsScalar minDepth; |
|
hsBool haveAxis = false; |
|
hsVector3 tstAxis; |
|
hsScalar tstDepth; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsBool tryAxis; |
|
if( other.fExtFlags & kAxisAligned ) |
|
{ |
|
// first try the other box axes |
|
hsScalar effMin = fCenter[i]; |
|
hsScalar effMax = effMin; |
|
hsScalar velDist = myVel[i]; |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
effMax += fRadius; |
|
effMin -= fRadius; |
|
|
|
if( effMax < other.fMins[i] ) |
|
return false; |
|
if( effMin > other.fMaxs[i] ) |
|
return false; |
|
|
|
if( (other.fMins[i] <= effMin) |
|
&&(other.fMaxs[i] <= effMax) ) |
|
{ |
|
tstDepth = other.fMaxs[i] - effMin; |
|
hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); |
|
tstAxis.Set(i == 0 ? hsScalar1 : 0, i & 1 ? hsScalar1 : 0, i & 2 ? hsScalar1 : 0); |
|
tryAxis = true; |
|
} |
|
else |
|
if( (other.fMins[i] >= effMin) |
|
&&(other.fMaxs[i] >= effMax) ) |
|
{ |
|
tstDepth = effMax - other.fMins[i]; |
|
hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); |
|
tstAxis.Set(i == 0 ? -hsScalar1 : 0, i & 1 ? -hsScalar1 : 0, i & 2 ? -hsScalar1 : 0); |
|
tryAxis = true; |
|
} |
|
else |
|
tryAxis = false; |
|
} |
|
else |
|
{ |
|
// first try the other box axes |
|
hsScalar radScaled = fRadius * other.fAxes[i].Magnitude(); |
|
hsScalar centerDist = other.fAxes[i].InnerProduct(fCenter); |
|
hsScalar effMin = centerDist; |
|
hsScalar effMax = centerDist; |
|
hsScalar velDist = other.fAxes[i].InnerProduct(myVel); |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
effMax += radScaled; |
|
effMin -= radScaled; |
|
|
|
if( !(other.fExtFlags & kDistsSet) ) |
|
other.IMakeDists(); |
|
|
|
if( effMax < other.fDists[i].fX ) |
|
return false; |
|
if( effMin > other.fDists[i].fY ) |
|
return false; |
|
|
|
if( centerDist <= other.fDists[i].fX ) |
|
{ |
|
tstDepth = effMax - other.fDists[i].fX; |
|
tstAxis = -other.fAxes[i]; |
|
hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); |
|
} |
|
else |
|
if( centerDist >= other.fDists[i].fY ) |
|
{ |
|
tstDepth = other.fDists[i].fY - effMin; |
|
tstAxis = other.fAxes[i]; |
|
hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); |
|
} |
|
else |
|
tryAxis = false; |
|
|
|
} |
|
if( tryAxis ) |
|
{ |
|
hsScalar magSq = tstAxis.MagnitudeSquared(); |
|
if( magSq > kRealSmall ) |
|
{ |
|
tstDepth *= tstDepth * hsScalarInvert(magSq); |
|
if( !haveAxis||(tstDepth < minDepth) ) |
|
{ |
|
minDepth = tstDepth; |
|
minAxis = tstAxis; |
|
haveAxis = true; |
|
} |
|
hsAssert(!haveAxis || (minAxis.MagnitudeSquared() > kRealSmall), "Bogus"); |
|
} |
|
} |
|
} |
|
// now try the axis between the center of sphere and center of other box |
|
hsVector3 diag(&fCenter, &other.GetCenter()); |
|
if( !haveAxis && (diag.MagnitudeSquared() < kRealSmall) ) |
|
diag.Set(1.f, 0, 0); |
|
hsScalar effMin = diag.InnerProduct(fCenter); |
|
hsScalar effMax = effMin; |
|
hsScalar velDist = diag.InnerProduct(myVel); |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
hsScalar radDist = fRadius * diag.Magnitude(); |
|
effMax += radDist; |
|
effMin -= radDist; |
|
hsPoint2 otherDepth; |
|
other.TestPlane(diag, otherDepth); |
|
if( effMax < otherDepth.fX ) |
|
return false; |
|
if( effMin > otherDepth.fY ) |
|
return false; |
|
|
|
tstAxis = diag; |
|
tstDepth = otherDepth.fY - effMin; |
|
hsScalar magSq = tstAxis.MagnitudeSquared(); |
|
if( magSq > 0 ) |
|
{ |
|
tstDepth *= tstDepth * hsScalarInvert(magSq); |
|
if( !haveAxis ||(tstDepth < minDepth) ) |
|
{ |
|
minDepth = tstDepth; |
|
minAxis = tstAxis; |
|
} |
|
} |
|
|
|
hsScalar invMag = hsScalarInvert(minAxis.Magnitude()); |
|
minAxis *= invMag; |
|
hsAssert(minDepth >= 0, "Late to find sep plane"); |
|
minDepth = hsSquareRoot(minDepth); |
|
hit->Set(this, &other, minAxis, minDepth); |
|
|
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (BoxBS)"); |
|
|
|
if( other.fExtFlags & kAxisAligned ) |
|
{ |
|
// first try the other box axes |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar effMin = fCenter[i]; |
|
hsScalar effMax = effMin; |
|
hsScalar velDist = myVel[i]; |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
effMax += fRadius; |
|
effMin -= fRadius; |
|
|
|
if( effMax < other.fMins[i] ) |
|
return false; |
|
if( effMin > other.fMaxs[i] ) |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
// first try the other box axes |
|
if( !(other.fExtFlags & kDistsSet) ) |
|
other.IMakeDists(); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar effMin = other.fAxes[i].InnerProduct(fCenter); |
|
hsScalar effMax = effMin; |
|
hsScalar velDist = other.fAxes[i].InnerProduct(myVel); |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
hsScalar radScaled = fRadius * other.fAxes[i].Magnitude(); |
|
effMax += radScaled; |
|
effMin -= radScaled; |
|
|
|
if( effMax < other.fDists[i].fX ) |
|
return false; |
|
if( effMin > other.fDists[i].fY ) |
|
return false; |
|
} |
|
} |
|
|
|
// now try the axis between the center of sphere and center of other box |
|
hsVector3 diag(&fCenter, &other.GetCenter()); |
|
hsScalar effMin = diag.InnerProduct(fCenter); |
|
hsScalar effMax = effMin; |
|
hsScalar velDist = diag.InnerProduct(myVel); |
|
if( velDist > 0 ) |
|
effMax += velDist; |
|
else |
|
effMin += velDist; |
|
hsScalar radDist = fRadius * diag.Magnitude(); |
|
effMax += radDist; |
|
effMin -= radDist; |
|
hsPoint2 otherDepth; |
|
other.TestPlane(diag, otherDepth); |
|
if( effMax < otherDepth.fX ) |
|
return false; |
|
if( effMin > otherDepth.fY ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectLine(const hsPoint3* from, const hsPoint3* at) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
|
|
hsPoint3 onLine; |
|
hsScalar z = ClosestPointToLine(&fCenter, from, at, &onLine); |
|
|
|
hsScalar distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); |
|
if( distSq >= fRadius*fRadius ) |
|
return false; |
|
|
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( ((*from)[i] < fMins[i])&&((*at)[i] < fMins[i]) ) |
|
return false; |
|
if( ((*from)[i] > fMaxs[i])&&((*at)[i] > fMaxs[i]) ) |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar d0 = fAxes[i].InnerProduct(from); |
|
hsScalar d1 = fAxes[i].InnerProduct(at); |
|
if( d0 < d1 ) |
|
{ |
|
if( d1 < fDists[i].fX ) |
|
return false; |
|
if( d0 > fDists[i].fY ) |
|
return false; |
|
} |
|
else |
|
{ |
|
if( d0 < fDists[i].fX ) |
|
return false; |
|
if( d1 > fDists[i].fY ) |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
hsBool hsBounds3Ext::ISectCone(const hsPoint3* from, const hsPoint3* at, hsScalar radius) const |
|
{ |
|
if( !(fExtFlags & kSphereSet) ) |
|
IMakeSphere(); |
|
|
|
// expensive |
|
hsPoint3 onLine; |
|
ClosestPointToLine(&fCenter, from, at, &onLine); |
|
|
|
hsScalar distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); |
|
hsScalar radiusSq = fRadius * fRadius; |
|
if (distSq - radius*radius >= radiusSq) |
|
return false; |
|
|
|
hsScalar dist = hsVector3(from, &onLine).Magnitude(); |
|
hsScalar len = hsVector3(from, at).Magnitude(); |
|
hsScalar partRadius = radius/len * dist; |
|
if (distSq - fRadius*fRadius - partRadius*partRadius >= 0) |
|
{ |
|
hsVector3 rayToCenter(&fCenter,&onLine); |
|
rayToCenter.Normalize(); |
|
|
|
hsPoint3 atEdge = *at + rayToCenter*radius; |
|
|
|
ClosestPointToLine(&fCenter, from, &atEdge, &onLine); |
|
|
|
distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); |
|
if( distSq >= radiusSq ) |
|
return false; |
|
} |
|
|
|
// incorrect |
|
if( fExtFlags & kAxisAligned ) |
|
{ |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( ((*from)[i] < fMins[i])&&((*at)[i]+radius < fMins[i]) ) |
|
return false; |
|
if( ((*from)[i] > fMaxs[i])&&((*at)[i]-radius > fMaxs[i]) ) |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
if( !(fExtFlags & kDistsSet) ) |
|
IMakeDists(); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
ClosestPointToInfiniteLine(at, &fAxes[i], &onLine); |
|
hsVector3 atLine(&onLine,at); |
|
atLine.Normalize(); |
|
hsPoint3 atEdge = *at + atLine * radius; |
|
|
|
hsScalar d0 = fAxes[i].InnerProduct(*from); |
|
hsScalar d1 = fAxes[i].InnerProduct(atEdge); |
|
if( d0 < d1 ) |
|
{ |
|
if( d1 < fDists[i].fX ) |
|
return false; |
|
if( d0 > fDists[i].fY ) |
|
return false; |
|
} |
|
else |
|
{ |
|
if( d0 < fDists[i].fX ) |
|
return false; |
|
if( d1 > fDists[i].fY ) |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
hsBool hsBounds3Ext::ISectRayBS(const hsPoint3& from, const hsPoint3& to, hsPoint3& at) const |
|
{ |
|
hsVector3 c2f(&from,&GetCenter()); |
|
hsVector3 f2t(&to,&from); |
|
hsScalar a = f2t.MagnitudeSquared(); |
|
hsScalar b = 2 * (c2f.InnerProduct(f2t)); |
|
hsScalar c = c2f.MagnitudeSquared() - GetRadius()*GetRadius(); |
|
|
|
hsScalar disc = b*b - 4*a*c; |
|
if (disc < 0) |
|
return false; |
|
else |
|
{ |
|
hsScalar discSqrt = hsSquareRoot(disc); |
|
hsScalar denom = 1.f/(2*a); |
|
hsScalar t = (-b - discSqrt) * denom; |
|
|
|
if (t<1 && t>0) |
|
at = from + (f2t * t); |
|
else |
|
return false; |
|
#if 0 |
|
{ |
|
t = (-b + discSqrt) * denom; |
|
if (t > 1) |
|
return false; |
|
at = from + (f2t * t); |
|
} |
|
#endif |
|
return true; |
|
} |
|
} |
|
|
|
void hsBounds3Ext::Read(hsStream *s) |
|
{ |
|
fExtFlags = s->ReadSwap32(); |
|
hsBounds3::Read(s); |
|
if( !(fExtFlags & kAxisAligned) ) |
|
{ |
|
fCorner.Read(s); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
fAxes[i].Read(s); |
|
fDists[i].fX = s->ReadSwapScalar(); |
|
fDists[i].fY = s->ReadSwapScalar(); |
|
} |
|
IMakeMinsMaxs(); |
|
IMakeDists(); |
|
} |
|
IMakeSphere(); |
|
} |
|
void hsBounds3Ext::Write(hsStream *s) |
|
{ |
|
s->WriteSwap32(fExtFlags); |
|
hsBounds3::Write(s); |
|
if( !(fExtFlags & kAxisAligned) ) |
|
{ |
|
fCorner.Write(s); |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
fAxes[i].Write(s); |
|
if( fExtFlags & kDistsSet ) |
|
{ |
|
s->WriteSwapScalar(fDists[i].fX); |
|
s->WriteSwapScalar(fDists[i].fY); |
|
} |
|
else |
|
{ |
|
// Playing nice with binary patches--writing uninited values BAD! |
|
s->WriteSwapScalar( 0.f ); |
|
s->WriteSwapScalar( 0.f ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration |
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
void hsBounds3Tri::TestPlane(const hsVector3 &n, hsPoint2 &depth) const |
|
{ |
|
depth.fX = depth.fY = n.InnerProduct(fVerts[0]); |
|
|
|
hsScalar d1, d2; |
|
|
|
d1 = n.InnerProduct(fVerts[1]); |
|
d2 = n.InnerProduct(fVerts[2]); |
|
|
|
if( d1 > d2 ) |
|
{ |
|
if( d1 > depth.fY ) |
|
depth.fY = d1; |
|
if( d2 < depth.fX ) |
|
depth.fX = d2; |
|
} |
|
else |
|
{ |
|
if( d2 > depth.fY ) |
|
depth.fY = d2; |
|
if( d1 < depth.fX ) |
|
depth.fX = d1; |
|
} |
|
} |
|
hsBool hsBounds3Tri::ClosestTriPoint(const hsPoint3 *p, hsPoint3 *out, const hsVector3 *ax) const |
|
{ |
|
// project point onto tri plane |
|
hsPoint3 pPln; |
|
if( ax ) |
|
{ |
|
hsScalar t; |
|
|
|
t = fNormal.InnerProduct(fVerts[0] - *p); |
|
hsScalar s = fNormal.InnerProduct(ax); |
|
if( (s > hsBounds::kRealSmall)||(s < -hsBounds::kRealSmall) ) |
|
{ |
|
t /= s; |
|
|
|
pPln = *p; |
|
pPln += *ax * t; |
|
} |
|
else |
|
{ |
|
return ClosestTriPoint(p, out); |
|
} |
|
} |
|
else |
|
{ |
|
hsScalar t; |
|
|
|
t = fNormal.InnerProduct(fVerts[0] - *p); |
|
t /= fNormal.MagnitudeSquared(); |
|
|
|
pPln = *p; |
|
pPln += fNormal * t; |
|
} |
|
|
|
if( !(fTriFlags & kAxesSet) ) |
|
SetAxes(); |
|
|
|
int nIn = 0; |
|
int firstIn, secondIn; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
hsScalar tst = fPerpAxes[i].InnerProduct(pPln); |
|
hsBool in = false; |
|
if( fOnIsMax & (1 << i) ) |
|
{ |
|
if( tst <= fPerpDists[i].fY ) |
|
in = true; |
|
} |
|
else |
|
{ |
|
if( tst >= fPerpDists[i].fX ) |
|
in = true; |
|
} |
|
if( in ) |
|
{ |
|
if( nIn++ ) |
|
secondIn = i; |
|
else |
|
firstIn = i; |
|
} |
|
} |
|
switch( nIn ) |
|
{ |
|
case 3: |
|
*out = pPln; |
|
break; |
|
case 1: |
|
{ |
|
int k, kPlus; |
|
k = firstIn == 2 ? 0 : firstIn+1; |
|
kPlus = k == 2 ? 0 : k+1; |
|
|
|
hsPoint3 pTmp; |
|
hsScalar z; |
|
z = hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, &pTmp); |
|
if( z <= hsScalar1 ) |
|
*out = pTmp; |
|
else |
|
{ |
|
k = kPlus; |
|
kPlus = k == 2 ? 0 : k+1; |
|
z = hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, out); |
|
} |
|
} |
|
break; |
|
case 2: |
|
{ |
|
int k, kPlus; |
|
k = secondIn == 2 ? 0 : secondIn+1; |
|
if( k == firstIn ) |
|
k++; |
|
kPlus = k == 2 ? 0 : k+1; |
|
|
|
hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, out); |
|
break; |
|
} |
|
|
|
case 0: |
|
hsAssert(false, "Extreme bogosity, inverted tri?!?"); |
|
*out = pPln; |
|
return false; |
|
} |
|
|
|
#ifdef HS_DEBUGGING // mf horse testing |
|
#if 0 |
|
if( 0 ) |
|
{ |
|
hsVector3 ndeb = hsVector3(fVerts+1, fVerts) % hsVector3(fVerts+2, fVerts); |
|
hsScalar dis; |
|
dis = fNormal.InnerProduct(pPln) - fDist; |
|
if( (fDist > hsBounds::kRealSmall)||(fDist < -hsBounds::kRealSmall) ) |
|
dis /= fDist; |
|
hsAssert((dis < hsBounds::kRealSmall)&&(dis > -hsBounds::kRealSmall), "Non-planar pPln"); |
|
dis = hsVector3(&pPln, out).MagnitudeSquared(); |
|
hsScalar vDis; |
|
vDis = hsVector3(&pPln, fVerts+0).MagnitudeSquared(); |
|
hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); |
|
vDis = hsVector3(&pPln, fVerts+1).MagnitudeSquared(); |
|
hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); |
|
vDis = hsVector3(&pPln, fVerts+2).MagnitudeSquared(); |
|
hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); |
|
hsBool dork = false; |
|
if( dork ) |
|
{ |
|
hsScalar zn[3]; |
|
hsScalar zf[3]; |
|
hsScalar z[3]; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
z[i] = fPerpAxes[i].InnerProduct(fVerts[i]); |
|
int j; |
|
j = i == 0 ? 2 : i-1; |
|
zf[i] = fPerpAxes[i].InnerProduct(fVerts[j]); |
|
j = i == 2 ? 0 : i+1; |
|
zn[i] = fPerpAxes[i].InnerProduct(fVerts[j]); |
|
} |
|
return ClosestTriPoint(p, out, ax); |
|
} |
|
} |
|
#endif |
|
#endif |
|
|
|
return 3 == nIn; |
|
} |
|
|
|
void hsBounds3Tri::SetAxes() const |
|
{ |
|
fOnIsMax = 0; |
|
|
|
hsVector3 edge[3]; |
|
edge[0].Set(fVerts, fVerts+1); |
|
edge[1].Set(fVerts+1, fVerts+2); |
|
edge[2].Set(fVerts+2, fVerts); |
|
hsVector3 perp = edge[2] % edge[0]; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
int j = i == 2 ? 0 : i+1; |
|
int k = j == 2 ? 0 : j+1; |
|
fPerpAxes[i] = edge[i] % perp; |
|
fPerpAxes[i].Normalize(); |
|
|
|
fPerpDists[i].fX = fPerpAxes[i].InnerProduct(fVerts[i]); |
|
fPerpDists[i].fY = fPerpAxes[i].InnerProduct(fVerts[k]); |
|
if( fPerpDists[i].fX > fPerpDists[i].fY ) |
|
{ |
|
fOnIsMax |= 1 << i; |
|
hsScalar d = fPerpDists[i].fX; |
|
fPerpDists[i].fX = fPerpDists[i].fY; |
|
fPerpDists[i].fY = d; |
|
} |
|
} |
|
fTriFlags |= kAxesSet; |
|
} |
|
|
|
hsBounds3Tri* hsBounds3Tri::Transform(const hsMatrix44& x) |
|
{ |
|
#if 0 // IDENT |
|
if( x.fFlags & hsMatrix44::kIsIdent ) |
|
return this; |
|
#endif // IDENT |
|
|
|
fVerts[0] = x * fVerts[0]; |
|
fVerts[1] = x * fVerts[1]; |
|
fVerts[2] = x * fVerts[2]; |
|
|
|
hsVector3 v1, v2; |
|
v1.Set(&fVerts[1], &fVerts[0]); |
|
v2.Set(&fVerts[2], &fVerts[0]); |
|
fNormal = v1 % v2; |
|
// mf horse - do we need to normalize here? |
|
// fNormal.Normalize(); |
|
|
|
fDist = fNormal.InnerProduct(fVerts[0]); |
|
|
|
fTriFlags &= ~kAxesSet; |
|
|
|
SetAxes(); |
|
|
|
return this; |
|
} |
|
|
|
hsBounds3Tri* hsBounds3Tri::Translate(const hsVector3& v) |
|
{ |
|
fVerts[0] += v; |
|
fVerts[1] += v; |
|
fVerts[2] += v; |
|
|
|
fDist = fNormal.InnerProduct(fVerts[0]); |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
int j = i == 2 ? 0 : i+1; |
|
int k = j == 2 ? 0 : j+1; |
|
|
|
hsScalar del = fPerpAxes[i].InnerProduct(v); |
|
fPerpDists[i].fX += del; |
|
fPerpDists[i].fY += del; |
|
} |
|
|
|
return this; |
|
} |
|
|
|
void hsBounds3Tri::Set(const hsPoint3& v0, |
|
const hsPoint3& v1, |
|
const hsPoint3& v2, |
|
hsTriangle3* t, |
|
const hsMatrix44& x) |
|
{ |
|
fVerts[0] = v0; |
|
fVerts[1] = v1; |
|
fVerts[2] = v2; |
|
|
|
fOnIsMax = 0; |
|
fTriangle = t; |
|
|
|
if( t->fFlags & hsTriangle3::kTwoSided ) |
|
fTriFlags |= kDoubleSide; |
|
|
|
#if 0 // IDENT |
|
if( x.fFlags & hsMatrix44::kIsIdent ) |
|
{ |
|
hsVector3 v1, v2; |
|
v1.Set(&fVerts[1], &fVerts[0]); |
|
v2.Set(&fVerts[2], &fVerts[0]); |
|
fNormal = v1 % v2; |
|
// mf horse - do we need to normalize here? |
|
// fNormal.Normalize(); |
|
|
|
fDist = fNormal.InnerProduct(fVerts[0]); |
|
|
|
fTriFlags &= ~kAxesSet; |
|
|
|
SetAxes(); |
|
} |
|
else |
|
#endif // IDENT |
|
Transform(x); |
|
} |
|
|
|
hsBounds3Tri::hsBounds3Tri(const hsPoint3& v0, |
|
const hsPoint3& v1, |
|
const hsPoint3& v2, |
|
hsTriangle3* t, |
|
const hsMatrix44& x) |
|
{ |
|
Set(v0, v1, v2, t, x); |
|
} |
|
|
|
hsBounds3Tri::hsBounds3Tri(hsTriangle3* t, const hsMatrix44& x) |
|
{ |
|
Set(t->fVert[0]->fVtx->fLocalPos, |
|
t->fVert[2]->fVtx->fLocalPos, |
|
t->fVert[2]->fVtx->fLocalPos, |
|
t, x); |
|
} |
|
|
|
void hsBounds3Tri::Set(hsPoint3 *v0, hsPoint3 *v1, hsPoint3 *v2, hsVector3 *n, UInt32 triFlags, hsTriangle3 *t) |
|
{ |
|
fTriFlags = 0; |
|
|
|
if( triFlags & hsTriangle3::kTwoSided ) |
|
fTriFlags |= kDoubleSide; |
|
|
|
fNormal = *n; |
|
fVerts[0] = *v0; |
|
fVerts[1] = *v1; |
|
fVerts[2] = *v2; |
|
|
|
fOnIsMax = 0; |
|
fTriangle = t; |
|
|
|
fDist = fNormal.InnerProduct(fVerts[0]); |
|
} |
|
|
|
hsBounds3Tri::hsBounds3Tri(hsPoint3 *v0, hsPoint3 *v1, hsPoint3 *v2, hsVector3 *n, UInt32 triFlags, hsTriangle3 *t) |
|
{ |
|
Set(v0, v1, v2, n, triFlags, t); |
|
} |
|
|
|
hsBounds3Tri::hsBounds3Tri(hsTriangle3* t) |
|
{ |
|
Set(&t->fVert[0]->fVtx->fLocalPos, |
|
&t->fVert[1]->fVtx->fLocalPos, |
|
&t->fVert[2]->fVtx->fLocalPos, |
|
&t->fNormal, t->fFlags, t); |
|
} |
|
|
|
hsBounds3Tri::~hsBounds3Tri() |
|
{ |
|
} |
|
|
|
|
|
// Finds closest intersection vertex or triangle/center-line intersection |
|
hsBool hsBounds3Tri::ISectCone(const hsPoint3& from, const hsPoint3& to, hsScalar cosThetaSq, hsBool ignoreFacing, hsPoint3& at, hsBool& backSide) const |
|
{ |
|
hsScalar d0 = from.InnerProduct(fNormal); |
|
hsScalar d1 = at.InnerProduct(fNormal); |
|
hsScalar dt = fNormal.InnerProduct(fVerts[0]); |
|
backSide = d0 < dt; |
|
if( !ignoreFacing && backSide ) |
|
return false; |
|
if ( (d0 < dt || d1 < dt) && |
|
(d0 > dt || d1 > dt) && |
|
ClosestTriPoint(&from, &at, &hsVector3(&to,&from)) ) |
|
return true; |
|
|
|
hsVector3 av(&to,&from); |
|
hsScalar distASq = av.MagnitudeSquared(); |
|
hsScalar radiusSq = distASq * (1-cosThetaSq)/cosThetaSq; |
|
|
|
hsScalar minDistSq = 0; |
|
Int32 minVert = 0; |
|
hsBool sect = false; |
|
for (Int32 i=0; i<3; i++) |
|
{ |
|
hsPoint3 onLine; |
|
hsScalar t = hsBounds3::ClosestPointToLine(&fVerts[i], &from, &to, &onLine); |
|
|
|
// outside the cap of the cylinder |
|
if (t<0 || t>1) |
|
continue; |
|
|
|
// outside the edge of the cylinder |
|
if (hsVector3(&onLine, &fVerts[i]).MagnitudeSquared() >= radiusSq) |
|
continue; |
|
|
|
hsVector3 bv(&fVerts[i],&from); |
|
|
|
hsScalar distBSq = bv.MagnitudeSquared(); |
|
|
|
hsScalar cosMuSquared = (av * bv) / (distASq * distBSq); |
|
|
|
// outside the angle of the cone |
|
if (cosMuSquared > cosThetaSq) |
|
continue; |
|
|
|
if (!sect || distBSq < minDistSq) |
|
{ |
|
minVert = i; |
|
minDistSq = distBSq; |
|
sect = true; |
|
} |
|
} |
|
at = fVerts[minVert]; |
|
return sect; |
|
} |
|
#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration
|
|
|