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.

990 lines
22 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 "plVolumeIsect.h"
#include "hsBounds.h"
#include "hsFastMath.h"
#include "hsStream.h"
#include "hsResMgr.h"
#include "plIntersect/plClosest.h"
static const hsScalar kDefLength = 5.f;
plSphereIsect::plSphereIsect()
: fRadius(1.f)
{
fCenter.Set(0,0,0);
int i;
for( i = 0; i < 3; i++ )
{
fMins[i] = -fRadius;
fMaxs[i] = fRadius;
}
}
plSphereIsect::~plSphereIsect()
{
}
void plSphereIsect::SetCenter(const hsPoint3& c)
{
fWorldCenter = fCenter = c;
int i;
for( i = 0; i < 3; i++ )
{
fMins[i] += c[i];
fMaxs[i] += c[i];
}
}
void plSphereIsect::SetRadius(hsScalar r)
{
hsScalar del = r - fRadius;
int i;
for( i = 0; i < 3; i++ )
{
fMins[i] -= del;
fMaxs[i] += del;
}
fRadius = r;
}
void plSphereIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
fWorldCenter = l2w * fCenter;
fMaxs = fMins = fWorldCenter;
int i;
for( i = 0; i < 3; i++ )
{
fMins[i] -= fRadius;
fMaxs[i] += fRadius;
}
}
// Could use ClosestPoint to find the closest point on the bounds
// to our center, and do a distance test on that. Would be more
// accurate than this box test approx, but whatever.
plVolumeCullResult plSphereIsect::Test(const hsBounds3Ext& bnd) const
{
const hsPoint3& maxs = bnd.GetMaxs();
const hsPoint3& mins = bnd.GetMins();
if( (maxs.fX < fMins.fX)
||
(maxs.fY < fMins.fY)
||
(maxs.fZ < fMins.fZ) )
return kVolumeCulled;
if( (mins.fX > fMaxs.fX)
||
(mins.fY > fMaxs.fY)
||
(mins.fZ > fMaxs.fZ) )
return kVolumeCulled;
if( (maxs.fX > fMaxs.fX)
||
(maxs.fY > fMaxs.fY)
||
(maxs.fZ > fMaxs.fZ) )
return kVolumeSplit;
if( (mins.fX < fMins.fX)
||
(mins.fY < fMins.fY)
||
(mins.fZ < fMins.fZ) )
return kVolumeSplit;
return kVolumeClear;
}
hsScalar plSphereIsect::Test(const hsPoint3& pos) const
{
hsScalar dist = (pos - fWorldCenter).MagnitudeSquared();
if( dist < fRadius*fRadius )
return 0;
dist = hsSquareRoot(dist);
return dist - fRadius;
}
void plSphereIsect::Read(hsStream* s, hsResMgr* mgr)
{
fCenter.Read(s);
fWorldCenter.Read(s);
fRadius = s->ReadSwapScalar();
fMins.Read(s);
fMaxs.Read(s);
}
void plSphereIsect::Write(hsStream* s, hsResMgr* mgr)
{
fCenter.Write(s);
fWorldCenter.Write(s);
s->WriteSwapScalar(fRadius);
fMins.Write(s);
fMaxs.Write(s);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plConeIsect::plConeIsect()
: fLength(kDefLength), fRadAngle(hsScalarPI*0.25f), fCapped(false)
{
ISetup();
}
plConeIsect::~plConeIsect()
{
}
void plConeIsect::SetAngle(hsScalar rads)
{
fRadAngle = rads;
ISetup();
}
void plConeIsect::ISetup()
{
hsScalar sinAng, cosAng;
hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng);
const hsScalar kHither = 0.1f;
fLightToNDC.Reset();
fLightToNDC.fMap[0][0] = hsScalarDiv( cosAng, sinAng );
fLightToNDC.fMap[1][1] = hsScalarDiv( cosAng, sinAng );
fLightToNDC.fMap[2][2] = -hsScalarDiv( fLength, fLength - kHither );
fLightToNDC.fMap[3][3] = hsIntToScalar( 0 );
fLightToNDC.fMap[3][2] = hsIntToScalar( -1 );
fLightToNDC.fMap[2][3] = -hsScalarMulDiv( fLength, kHither, fLength - kHither );
fLightToNDC.NotIdentity();
}
plVolumeCullResult plConeIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult retVal = kVolumeClear;
hsPoint2 depth;
hsVector3 normDir = -fWorldNorm;
bnd.TestPlane(normDir, depth);
if( depth.fY < normDir.InnerProduct(fWorldTip) )
return kVolumeCulled;
int last = fCapped ? 5 : 4;
int i;
for( i = 0; i < last; i++ )
{
bnd.TestPlane(fNorms[i], depth);
if( depth.fY + fDists[i] <= 0 )
return kVolumeCulled;
if( depth.fX + fDists[i] <= 0 )
retVal = kVolumeSplit;
}
if( retVal == kVolumeSplit )
{
hsVector3 axis = normDir % hsVector3(&bnd.GetCenter(), &fWorldTip);
hsFastMath::NormalizeAppr(axis);
hsVector3 perp = axis % normDir;
hsScalar sinAng, cosAng;
hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng);
hsVector3 tangent = normDir + sinAng * perp + (1-cosAng) * (axis % perp);
hsVector3 normIn = tangent % axis;
hsVector3 normIn2 = perp + sinAng * (perp % axis) + (1-cosAng) * (axis % (axis % perp));
bnd.TestPlane(normIn, depth);
hsScalar normInDotTip = normIn.InnerProduct(fWorldTip);
if( depth.fY < normInDotTip )
return kVolumeCulled;
}
return retVal;
}
hsScalar plConeIsect::Test(const hsPoint3& pos) const
{
UInt32 clampFlags = fCapped ? plClosest::kClamp : plClosest::kClampLower;
hsPoint3 cp;
plClosest::PointOnLine(pos,
fWorldTip, fWorldNorm,
cp,
clampFlags);
hsScalar radDist = (pos - cp).Magnitude();
hsScalar axDist = fWorldNorm.InnerProduct(pos - fWorldTip) / fLength;
if( axDist < 0 )
{
return radDist;
}
hsScalar sinAng, cosAng;
hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng);
hsScalar radius = axDist * sinAng / cosAng;
radDist -= radius;
axDist -= fLength;
if( fCapped && (axDist > 0) )
{
return axDist > radDist ? axDist : radDist;
}
return radDist > 0 ? radDist : 0;
}
//#define MF_DEBUG_NORM
#ifdef MF_DEBUG_NORM
#define IDEBUG_NORMALIZE( a, b ) { hsScalar len = 1.f / a.Magnitude(); a *= len; b *= len; }
#else // MF_DEBUG_NORM
#define IDEBUG_NORMALIZE( a, b )
#endif // MF_DEBUG_NORM
void plConeIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
fWorldTip = l2w.GetTranslate();
fWorldNorm.Set(l2w.fMap[0][2], l2w.fMap[1][2], l2w.fMap[2][2]);
fWorldToNDC = fLightToNDC * w2l;
int i;
for( i = 0; i < 2; i++ )
{
fNorms[i].Set(fWorldToNDC.fMap[3][0] - fWorldToNDC.fMap[i][0], fWorldToNDC.fMap[3][1] - fWorldToNDC.fMap[i][1], fWorldToNDC.fMap[3][2] - fWorldToNDC.fMap[i][2]);
fDists[i] = fWorldToNDC.fMap[3][3] - fWorldToNDC.fMap[i][3];
IDEBUG_NORMALIZE( fNorms[i], fDists[i] );
fNorms[i+2].Set(fWorldToNDC.fMap[3][0] + fWorldToNDC.fMap[i][0], fWorldToNDC.fMap[3][1] + fWorldToNDC.fMap[i][1], fWorldToNDC.fMap[3][2] + fWorldToNDC.fMap[i][2]);
fDists[i+2] = fWorldToNDC.fMap[3][3] + fWorldToNDC.fMap[i][3];
IDEBUG_NORMALIZE( fNorms[i+2], fDists[i+2] );
}
if( fCapped )
{
fNorms[4].Set(fWorldToNDC.fMap[3][0] - fWorldToNDC.fMap[2][0], fWorldToNDC.fMap[3][1] - fWorldToNDC.fMap[2][1], fWorldToNDC.fMap[3][2] - fWorldToNDC.fMap[2][2]);
fDists[4] = fWorldToNDC.fMap[3][3] - fWorldToNDC.fMap[2][3];
IDEBUG_NORMALIZE( fNorms[4], fDists[4] );
}
}
void plConeIsect::SetLength(hsScalar d)
{
if( d > 0 )
{
fCapped = true;
fLength = d;
}
else
{
fCapped = false;
fLength = kDefLength;
}
ISetup();
}
void plConeIsect::Read(hsStream* s, hsResMgr* mgr)
{
fCapped = s->ReadSwap32();
fRadAngle = s->ReadSwapScalar();
fLength = s->ReadSwapScalar();
fWorldTip.Read(s);
fWorldNorm.Read(s);
fWorldToNDC.Read(s);
fLightToNDC.Read(s);
int n = fCapped ? 5 : 4;
int i;
for(i = 0; i < n; i++ )
{
fNorms[i].Read(s);
fDists[i] = s->ReadSwapScalar();
}
}
void plConeIsect::Write(hsStream* s, hsResMgr* mgr)
{
s->WriteSwap32(fCapped);
s->WriteSwapScalar(fRadAngle);
s->WriteSwapScalar(fLength);
fWorldTip.Write(s);
fWorldNorm.Write(s);
fWorldToNDC.Write(s);
fLightToNDC.Write(s);
int n = fCapped ? 5 : 4;
int i;
for(i = 0; i < n; i++ )
{
fNorms[i].Write(s);
s->WriteSwapScalar(fDists[i]);
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plCylinderIsect::plCylinderIsect()
{
}
plCylinderIsect::~plCylinderIsect()
{
}
void plCylinderIsect::ISetupCyl(const hsPoint3& wTop, const hsPoint3& wBot, hsScalar radius)
{
fWorldNorm.Set(&wTop, &wBot);
fLength = fWorldNorm.Magnitude();
fMin = fWorldNorm.InnerProduct(wBot);
fMax = fWorldNorm.InnerProduct(wTop);
if( fMin > fMax )
{
hsScalar t = fMin;
fMin = fMax;
fMax = t;
}
fRadius = radius;
}
void plCylinderIsect::SetCylinder(const hsPoint3& lTop, const hsPoint3& lBot, hsScalar radius)
{
fTop = lTop;
fBot = lBot;
fRadius = radius;
ISetupCyl(fTop, fBot, fRadius);
}
void plCylinderIsect::SetCylinder(const hsPoint3& lBot, const hsVector3& axis, hsScalar radius)
{
fBot = lBot;
fTop = fBot;
fTop += axis;
ISetupCyl(fTop, fBot, radius);
}
void plCylinderIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
hsPoint3 wTop = l2w * fTop;
hsPoint3 wBot = l2w * fBot;
ISetupCyl(wTop, wBot, fRadius);
}
plVolumeCullResult plCylinderIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult radVal = kVolumeClear;
// Central axis test
hsPoint2 depth;
bnd.TestPlane(fWorldNorm, depth);
if( depth.fX > fMax )
return kVolumeCulled;
if( depth.fY < fMin )
return kVolumeCulled;
if( (depth.fX < fMin)
||(depth.fY > fMax) )
{
radVal = kVolumeSplit;
}
// Radial test
plVolumeCullResult retVal = kVolumeCulled;
// Find the closest point on/in the bounds to our central axis.
// If that closest point is inside the cylinder, we have a hit.
hsPoint3 corner;
bnd.GetCorner(&corner);
hsVector3 axes[3];
bnd.GetAxes(axes+0, axes+1, axes+2);
hsPoint3 cp = corner;
hsScalar bndRadiusSq = bnd.GetRadius();
bndRadiusSq *= bndRadiusSq;
hsScalar radiusSq = fRadius*fRadius;
hsScalar maxClearDistSq = fRadius - bnd.GetRadius();
maxClearDistSq *= maxClearDistSq;
int i;
for( i = 0; i < 3; i++ )
{
hsPoint3 cp0;
hsPoint3 currPt;
plClosest::PointsOnLines(fWorldBot, fWorldNorm,
cp, axes[i],
cp0, currPt,
plClosest::kClamp);
hsScalar distSq = (cp0 - currPt).MagnitudeSquared();
if( distSq < radiusSq )
{
if( distSq < maxClearDistSq )
{
return kVolumeClear == radVal ? kVolumeClear : kVolumeSplit;
}
retVal = kVolumeSplit;
}
cp = currPt;
}
return retVal;
}
hsScalar plCylinderIsect::Test(const hsPoint3& pos) const
{
hsPoint3 cp;
plClosest::PointOnLine(pos,
fWorldBot, fWorldNorm,
cp,
plClosest::kClamp);
hsScalar radDist = (pos - cp).Magnitude() - fRadius;
hsScalar axDist = fWorldNorm.InnerProduct(pos - fWorldBot) / fLength;
if( axDist < 0 )
axDist = -axDist;
else
axDist -= fLength;
hsScalar dist = axDist > radDist ? axDist : radDist;
return dist > 0 ? dist : 0;
}
void plCylinderIsect::Read(hsStream* s, hsResMgr* mgr)
{
fTop.Read(s);
fBot.Read(s);
fRadius = s->ReadSwapScalar();
fWorldBot.Read(s);
fWorldNorm.Read(s);
fLength = s->ReadSwapScalar();
fMin = s->ReadSwapScalar();
fMax = s->ReadSwapScalar();
}
void plCylinderIsect::Write(hsStream* s, hsResMgr* mgr)
{
fTop.Write(s);
fBot.Write(s);
s->WriteSwapScalar(fRadius);
fWorldBot.Write(s);
fWorldNorm.Write(s);
s->WriteSwapScalar(fLength);
s->WriteSwapScalar(fMin);
s->WriteSwapScalar(fMax);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plParallelIsect::plParallelIsect()
{
}
plParallelIsect::~plParallelIsect()
{
}
void plParallelIsect::SetNumPlanes(int n)
{
fPlanes.SetCount(n);
}
void plParallelIsect::SetPlane(int which, const hsPoint3& locPosOne, const hsPoint3& locPosTwo)
{
fPlanes[which].fPosOne = locPosOne;
fPlanes[which].fPosTwo = locPosTwo;
}
void plParallelIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsPoint3 wPosOne = l2w * fPlanes[i].fPosOne;
hsPoint3 wPosTwo = l2w * fPlanes[i].fPosTwo;
hsVector3 norm;
norm.Set(&wPosOne, &wPosTwo);
fPlanes[i].fNorm = norm;
hsScalar t0 = norm.InnerProduct(wPosOne);
hsScalar t1 = norm.InnerProduct(wPosTwo);
if( t0 > t1 )
{
fPlanes[i].fMin = t1;
fPlanes[i].fMax = t0;
}
else
{
fPlanes[i].fMin = t0;
fPlanes[i].fMax = t1;
}
}
}
plVolumeCullResult plParallelIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult retVal = kVolumeClear;
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsPoint2 depth;
bnd.TestPlane(fPlanes[i].fNorm, depth);
if( depth.fY < fPlanes[i].fMin )
return kVolumeCulled;
if( depth.fX > fPlanes[i].fMax )
return kVolumeCulled;
if( depth.fX < fPlanes[i].fMin )
retVal = kVolumeSplit;
if( depth.fY > fPlanes[i].fMax )
retVal = kVolumeSplit;
}
return retVal;
}
hsScalar plParallelIsect::Test(const hsPoint3& pos) const
{
hsScalar maxDist = 0;
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsScalar dist = fPlanes[i].fNorm.InnerProduct(pos);
if( dist > fPlanes[i].fMax )
{
dist -= fPlanes[i].fMax;
dist *= hsFastMath::InvSqrtAppr(fPlanes[i].fNorm.MagnitudeSquared());
if( dist > maxDist )
maxDist = dist;
}
else if( dist < fPlanes[i].fMin )
{
dist = fPlanes[i].fMin - dist;
dist *= hsFastMath::InvSqrtAppr(fPlanes[i].fNorm.MagnitudeSquared());
if( dist > maxDist )
maxDist = dist;
}
}
return maxDist;
}
void plParallelIsect::Read(hsStream* s, hsResMgr* mgr)
{
int n = s->ReadSwap16();
fPlanes.SetCount(n);
int i;
for( i = 0; i < n; i++ )
{
fPlanes[i].fNorm.Read(s);
fPlanes[i].fMin = s->ReadSwapScalar();
fPlanes[i].fMax = s->ReadSwapScalar();
fPlanes[i].fPosOne.Read(s);
fPlanes[i].fPosTwo.Read(s);
}
}
void plParallelIsect::Write(hsStream* s, hsResMgr* mgr)
{
s->WriteSwap16(fPlanes.GetCount());
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
fPlanes[i].fNorm.Write(s);
s->WriteSwapScalar(fPlanes[i].fMin);
s->WriteSwapScalar(fPlanes[i].fMax);
fPlanes[i].fPosOne.Write(s);
fPlanes[i].fPosTwo.Write(s);
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plConvexIsect::plConvexIsect()
{
}
plConvexIsect::~plConvexIsect()
{
}
void plConvexIsect::AddPlaneUnchecked(const hsVector3& n, hsScalar dist)
{
SinglePlane plane;
plane.fNorm = n;
plane.fPos.Set(0,0,0);
plane.fDist = dist;
fPlanes.Append(plane);
}
void plConvexIsect::AddPlane(const hsVector3& n, const hsPoint3& p)
{
hsVector3 nNorm = n;
hsFastMath::Normalize(nNorm);
// First, make sure some idiot isn't adding the same plane in twice.
// Also, look for the degenerate case of two parallel planes. In that
// case, take the outer.
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
const hsScalar kCloseToOne = 1.f - 1.e-4f;
if( fPlanes[i].fNorm.InnerProduct(nNorm) >= kCloseToOne )
{
hsScalar dist = nNorm.InnerProduct(p);
if( dist > fPlanes[i].fDist )
{
fPlanes[i].fDist = dist;
fPlanes[i].fPos = p;
}
return;
}
}
SinglePlane plane;
plane.fNorm = nNorm;
plane.fPos = p;
plane.fDist = nNorm.InnerProduct(p);
fPlanes.Append(plane);
}
void plConvexIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsPoint3 wPos = l2w * fPlanes[i].fPos;
// Normal gets transpose of inverse.
fPlanes[i].fWorldNorm.fX = w2l.fMap[0][0] * fPlanes[i].fNorm.fX
+ w2l.fMap[1][0] * fPlanes[i].fNorm.fY
+ w2l.fMap[2][0] * fPlanes[i].fNorm.fZ;
fPlanes[i].fWorldNorm.fY = w2l.fMap[0][1] * fPlanes[i].fNorm.fX
+ w2l.fMap[1][1] * fPlanes[i].fNorm.fY
+ w2l.fMap[2][1] * fPlanes[i].fNorm.fZ;
fPlanes[i].fWorldNorm.fZ = w2l.fMap[0][2] * fPlanes[i].fNorm.fX
+ w2l.fMap[1][2] * fPlanes[i].fNorm.fY
+ w2l.fMap[2][2] * fPlanes[i].fNorm.fZ;
hsFastMath::NormalizeAppr(fPlanes[i].fWorldNorm);
fPlanes[i].fWorldDist = fPlanes[i].fWorldNorm.InnerProduct(wPos);
}
}
plVolumeCullResult plConvexIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult retVal = kVolumeClear;
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsPoint2 depth;
bnd.TestPlane(fPlanes[i].fWorldNorm, depth);
if( depth.fX > fPlanes[i].fWorldDist )
return kVolumeCulled;
if( depth.fY > fPlanes[i].fWorldDist )
retVal = kVolumeSplit;
}
return retVal;
}
hsScalar plConvexIsect::Test(const hsPoint3& pos) const
{
hsScalar maxDist = 0;
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
hsScalar dist = fPlanes[i].fWorldNorm.InnerProduct(pos) - fPlanes[i].fWorldDist;
if( dist > maxDist )
maxDist = dist;
}
return maxDist;
}
void plConvexIsect::Read(hsStream* s, hsResMgr* mgr)
{
Int16 n = s->ReadSwap16();
fPlanes.SetCount(n);
int i;
for( i = 0; i < n; i++ )
{
fPlanes[i].fNorm.Read(s);
fPlanes[i].fPos.Read(s);
fPlanes[i].fDist = s->ReadSwapScalar();
fPlanes[i].fWorldNorm.Read(s);
fPlanes[i].fWorldDist = s->ReadSwapScalar();
}
}
void plConvexIsect::Write(hsStream* s, hsResMgr* mgr)
{
s->WriteSwap16(fPlanes.GetCount());
int i;
for( i = 0; i < fPlanes.GetCount(); i++ )
{
fPlanes[i].fNorm.Write(s);
fPlanes[i].fPos.Write(s);
s->WriteSwapScalar(fPlanes[i].fDist);
fPlanes[i].fWorldNorm.Write(s);
s->WriteSwapScalar(fPlanes[i].fWorldDist);
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plBoundsIsect::plBoundsIsect()
{
}
plBoundsIsect::~plBoundsIsect()
{
}
void plBoundsIsect::SetBounds(const hsBounds3Ext& bnd)
{
fLocalBounds = bnd;
fWorldBounds = bnd;
}
void plBoundsIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
fWorldBounds = fLocalBounds;
fWorldBounds.Transform(&l2w);
}
plVolumeCullResult plBoundsIsect::Test(const hsBounds3Ext& bnd) const
{
int retVal = fWorldBounds.TestBound(bnd);
if( retVal < 0 )
return kVolumeCulled;
if( retVal > 0 )
return kVolumeClear;
retVal = bnd.TestBound(fWorldBounds);
return retVal < 0 ? kVolumeCulled : kVolumeSplit;
}
hsScalar plBoundsIsect::Test(const hsPoint3& pos) const
{
hsAssert(false, "Unimplemented");
return 0.f;
}
void plBoundsIsect::Read(hsStream* s, hsResMgr* mgr)
{
fLocalBounds.Read(s);
fWorldBounds.Read(s);
}
void plBoundsIsect::Write(hsStream* s, hsResMgr* mgr)
{
fLocalBounds.Write(s);
fWorldBounds.Write(s);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plComplexIsect::plComplexIsect()
{
}
plComplexIsect::~plComplexIsect()
{
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
delete fVolumes[i];
}
void plComplexIsect::AddVolume(plVolumeIsect* v)
{
fVolumes.Append(v);
}
void plComplexIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
fVolumes[i]->SetTransform(l2w, w2l);
}
void plComplexIsect::Read(hsStream* s, hsResMgr* mgr)
{
int n = s->ReadSwap16();
fVolumes.SetCount(n);
int i;
for( i = 0; i < n; i++ )
{
fVolumes[i] = plVolumeIsect::ConvertNoRef(mgr->ReadCreatable(s));
hsAssert(fVolumes[i], "Failure reading in a sub-volume");
}
}
void plComplexIsect::Write(hsStream* s, hsResMgr* mgr)
{
s->WriteSwap16(fVolumes.GetCount());
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
mgr->WriteCreatable(s, fVolumes[i]);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plUnionIsect::plUnionIsect()
{
}
plUnionIsect::~plUnionIsect()
{
}
plVolumeCullResult plUnionIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult retVal = kVolumeCulled;
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
{
plVolumeCullResult ret = fVolumes[i]->Test(bnd);
switch( ret )
{
case kVolumeCulled:
break;
case kVolumeClear:
return kVolumeClear;
case kVolumeSplit:
retVal = kVolumeSplit;
break;
};
}
return retVal;
}
hsScalar plUnionIsect::Test(const hsPoint3& pos) const
{
hsScalar retVal = 1.e33f;
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
{
hsScalar ret = fVolumes[i]->Test(pos);
if( ret <= 0 )
return 0;
if( ret < retVal )
retVal = ret;
}
return retVal;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
plIntersectionIsect::plIntersectionIsect()
{
}
plIntersectionIsect::~plIntersectionIsect()
{
}
plVolumeCullResult plIntersectionIsect::Test(const hsBounds3Ext& bnd) const
{
plVolumeCullResult retVal = kVolumeClear;
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
{
plVolumeCullResult ret = fVolumes[i]->Test(bnd);
switch( ret )
{
case kVolumeCulled:
return kVolumeCulled;
case kVolumeClear:
break;
case kVolumeSplit:
retVal = kVolumeSplit;
break;
};
}
return retVal;
}
hsScalar plIntersectionIsect::Test(const hsPoint3& pos) const
{
hsScalar retVal = -1.f;
int i;
for( i = 0; i < fVolumes.GetCount(); i++ )
{
hsScalar ret = fVolumes[i]->Test(pos);
if( ret > retVal )
retVal = ret;
}
return retVal;
}