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.
314 lines
8.9 KiB
314 lines
8.9 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 "hsGeometry3.h" |
|
#include "plTriUtils.h" |
|
|
|
static const hsScalar kAlmostZero = 1.e-5f; |
|
static const hsScalar kPastZero = -kAlmostZero; |
|
static const hsScalar kPastOne = 1.f + kAlmostZero; |
|
static const hsScalar kAlmostOne = 1.f - kAlmostZero; |
|
static const hsScalar kAlmostZeroSquared = kAlmostZero*kAlmostZero; |
|
|
|
static inline hsVector3 Cross(const hsScalarTriple& p0, const hsScalarTriple& p1) |
|
{ |
|
return hsVector3(p0.fY * p1.fZ - p0.fZ * p1.fY, |
|
p0.fZ * p1.fX - p0.fX * p1.fZ, |
|
p0.fX * p1.fY - p0.fY * p1.fX); |
|
} |
|
|
|
|
|
// There's actually a possibly faster way to do all this. |
|
// The barycentric coordinate in 3-space is the same as the barycentric coordinate of the projection |
|
// in 2-space, as long as the projection doesn't degenerate the triangle (i.e. project the tri onto |
|
// a plane perpindicular to the tri). The tri can't be perpindicular to all three major axes, so by |
|
// picking the right one (or just not picking the wrong one), the lengths of the cross products becomes |
|
// just the z component (e.g. v0.x*v1.y - v0.y*v1.x), so all the square roots go away (not to mention all |
|
// the vector math going from 3 component to 2). |
|
plTriUtils::Bary plTriUtils::ComputeBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out) |
|
{ |
|
hsVector3 v12(&p1, &p2); |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 norm = Cross(v12, v02); |
|
|
|
hsScalar invLenSq12 = norm.MagnitudeSquared(); |
|
if( invLenSq12 < kAlmostZero ) |
|
return kDegenerateTri; // degenerate triangle |
|
|
|
invLenSq12 = 1.f / invLenSq12; |
|
|
|
p += norm * (hsVector3(&p2, &p).InnerProduct(norm) * invLenSq12); |
|
|
|
hsVector3 vp2(&p, &p2); |
|
hsVector3 v0 = Cross(v12, vp2); |
|
hsVector3 v1 = Cross(vp2, v02); |
|
|
|
return IComputeBarycentric(norm, invLenSq12, v0, v1, out); |
|
} |
|
|
|
plTriUtils::Bary plTriUtils::ComputeBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) |
|
{ |
|
hsVector3 v12(&p1, &p2); |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 norm = Cross(v12, v02); |
|
hsScalar invLenSq12 = norm.MagnitudeSquared(); |
|
if( invLenSq12 < kAlmostZero ) |
|
return kDegenerateTri; // degenerate triangle |
|
|
|
invLenSq12 = 1.f / invLenSq12; |
|
|
|
hsVector3 vp2(&p, &p2); |
|
hsVector3 v0 = Cross(v12, vp2); |
|
hsVector3 v1 = Cross(vp2, v02); |
|
|
|
return IComputeBarycentric(norm, invLenSq12, v0, v1, out); |
|
} |
|
|
|
|
|
plTriUtils::Bary plTriUtils::IComputeBarycentric(const hsVector3& v12, hsScalar invLenSq12, const hsVector3& v0, const hsVector3& v1, hsPoint3& out) |
|
{ |
|
UInt32 state = 0; |
|
|
|
hsScalar lenSq0 = v0.MagnitudeSquared(); |
|
if( lenSq0 < kAlmostZeroSquared ) |
|
{ |
|
// On edge p1-p2; |
|
out[0] = 0; |
|
state |= kOnEdge12; |
|
} |
|
else |
|
{ |
|
out[0] = lenSq0 * invLenSq12; |
|
out[0] = hsSquareRoot(out[0]); |
|
// |
|
if( v0.InnerProduct(v12) < 0 ) |
|
{ |
|
out[0] = -out[0]; |
|
state |= kOutsideTri; |
|
} |
|
else if( out[0] > kPastOne ) |
|
state |= kOutsideTri; |
|
else if( out[0] > kAlmostOne ) |
|
state |= kOnVertex0; |
|
} |
|
|
|
hsScalar lenSq1 = v1.MagnitudeSquared(); |
|
if( lenSq1 < kAlmostZeroSquared ) |
|
{ |
|
// On edge p0-p2 |
|
out[1] = 0; |
|
state |= kOnEdge02; |
|
} |
|
else |
|
{ |
|
out[1] = lenSq1 * invLenSq12; |
|
out[1] = hsSquareRoot(out[1]); |
|
|
|
if( v1.InnerProduct(v12) < 0 ) |
|
{ |
|
out[1] = -out[1]; |
|
state |= kOutsideTri; |
|
} |
|
else if( out[1] > kPastOne ) |
|
state |= kOutsideTri; |
|
else if( out[1] > kAlmostOne ) |
|
state |= kOnVertex1; |
|
} |
|
|
|
// Could make more robust against precision problems |
|
// by repeating above for out[2], then normalizing |
|
// so sum(out[i]) = 1.f |
|
out[2] = 1.f - out[0] - out[1]; |
|
|
|
if( out[2] < kPastZero ) |
|
state |= kOutsideTri; |
|
else if( out[2] < kAlmostZero ) |
|
state |= kOnEdge01; |
|
else if( out[2] > kAlmostOne ) |
|
state |= kOnVertex2; |
|
|
|
/* |
|
if( a,b,c outside range [0..1] ) |
|
p is outside tri; |
|
else if( a,b,c == 1 ) |
|
p is on vert; |
|
else if( a,b,c == 0 ) |
|
p is on edge; |
|
*/ |
|
|
|
if( state & kOutsideTri ) |
|
return kOutsideTri; |
|
|
|
if( state & kOnVertex ) |
|
return Bary(state & kOnVertex); |
|
|
|
if( state & kOnEdge ) |
|
return Bary(state & kOnEdge); |
|
|
|
return kInsideTri; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
int plTriUtils::ISelectAxis(const hsVector3& norm) |
|
{ |
|
int retVal = -2; |
|
hsScalar maxDim = 0; |
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( norm[i] > maxDim ) |
|
{ |
|
maxDim = norm[i]; |
|
retVal = i; |
|
} |
|
else if( -norm[i] > maxDim ) |
|
{ |
|
maxDim = -norm[i]; |
|
retVal = i; |
|
} |
|
} |
|
return retVal; |
|
} |
|
|
|
hsBool plTriUtils::IFastBarycentric(int iAx, const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) |
|
{ |
|
if( --iAx < 0 ) |
|
iAx = 2; |
|
int jAx = iAx - 1; |
|
if( jAx < 0 ) |
|
jAx = 2; |
|
|
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 v12(&p1, &p2); |
|
|
|
hsScalar totArea = v02[iAx] * v12[jAx] - v02[jAx] * v12[iAx]; |
|
hsAssert(totArea != 0, "Should have already filtered degerate tris and degenerate projection"); |
|
|
|
hsScalar invTotArea = 1.f / totArea; |
|
|
|
hsVector3 vp2(&p, &p2); |
|
|
|
hsScalar aArea = vp2[iAx] * v12[jAx] - vp2[jAx] * v12[iAx]; |
|
|
|
hsScalar bArea = v02[iAx] * vp2[jAx] - v02[jAx] * vp2[iAx]; |
|
|
|
out[0] = aArea * invTotArea; |
|
out[1] = bArea * invTotArea; |
|
out[2] = 1.f - out[0] - out[1]; |
|
|
|
return true; |
|
} |
|
|
|
hsBool plTriUtils::FastBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out) |
|
{ |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 v12(&p1, &p2); |
|
|
|
hsVector3 norm = Cross(v12, v02); |
|
hsScalar invLenSq12 = norm.MagnitudeSquared(); |
|
if( invLenSq12 < kAlmostZero ) |
|
return false; // degenerate triangle |
|
|
|
invLenSq12 = 1.f / invLenSq12; |
|
|
|
hsVector3 del(&p0, &p); |
|
hsScalar delDotNormOverLenSq = del.InnerProduct(norm) * invLenSq12; |
|
|
|
p += norm * delDotNormOverLenSq; |
|
|
|
int iAx = ISelectAxis(norm); |
|
hsAssert(iAx >= 0, "Should have already picked out degenerate tris"); |
|
|
|
return IFastBarycentric(iAx, p0, p1, p2, p, out); |
|
} |
|
|
|
hsBool plTriUtils::FastBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) |
|
{ |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 v12(&p1, &p2); |
|
int iAx = ISelectAxis(Cross(v12, v02)); |
|
if( iAx < 0 ) |
|
return false; |
|
return IFastBarycentric(iAx, p0, p1, p2, p, out); |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
hsBool plTriUtils::ProjectOntoPlane(const hsVector3& norm, hsScalar dist, hsPoint3& p) |
|
{ |
|
hsScalar normMagSq = norm.MagnitudeSquared(); |
|
if( normMagSq > kAlmostZero ) |
|
{ |
|
dist /= normMagSq; |
|
|
|
p += norm * dist; |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
hsBool plTriUtils::ProjectOntoPlane(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3& p) |
|
{ |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 v12(&p1, &p2); |
|
|
|
hsVector3 norm = v12 % v02; |
|
|
|
hsScalar dist = norm.InnerProduct(p0 - p); |
|
|
|
return ProjectOntoPlane(norm, dist, p); |
|
} |
|
|
|
hsBool plTriUtils::ProjectOntoPlaneAlongVector(const hsVector3& norm, hsScalar dist, const hsVector3& vec, hsPoint3& p) |
|
{ |
|
hsScalar s = norm.InnerProduct(vec); |
|
const hsScalar kAlmostZero = 1.e-5f; |
|
if( (s > kAlmostZero)||(s < kPastZero) ) |
|
{ |
|
dist /= s; |
|
|
|
p += vec * dist; |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
hsBool plTriUtils::ProjectOntoPlaneAlongVector(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsVector3& vec, hsPoint3& p) |
|
{ |
|
hsVector3 v02(&p0, &p2); |
|
hsVector3 v12(&p1, &p2); |
|
|
|
hsVector3 norm = v12 % v02; |
|
hsScalar dist = norm.InnerProduct(p0 - p); |
|
|
|
return ProjectOntoPlaneAlongVector(norm, dist, vec, p); |
|
} |