|
|
|
/*==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/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|