diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp')
| -rw-r--r-- | APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp | 3020 |
1 files changed, 3020 insertions, 0 deletions
diff --git a/APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp b/APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp new file mode 100644 index 00000000..167e8e24 --- /dev/null +++ b/APEX_1.4/shared/general/HACD/src/ConvexDecomposition.cpp @@ -0,0 +1,3020 @@ +/*! +** +** Copyright (c) 2015 by John W. Ratcliff mailto:[email protected] +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +** +** If you find this code snippet useful; you can tip me at this bitcoin address: +** +** BITCOIN TIP JAR: "1BT66EoaGySkbY9J6MugvQRhMMXDwPxPya" +** + + + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <float.h> +#include <math.h> + +#include "ConvexDecomposition.h" +#include "ConvexHull.h" +#include "dgConvexHull3d.h" + +const float FM_PI = 3.1415926535897932384626433832795028841971693993751f; +const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f); + + +typedef hacd::vector< uint32_t > UintVector; + +namespace CONVEX_DECOMPOSITION +{ +class ConvexDecompInterface +{ +public: + virtual void ConvexDecompResult(const ConvexResult &result) = 0; +}; + + + +class Cdesc +{ +public: + ConvexDecompInterface *mCallback; + float mMeshVolumePercent; + float mMasterVolume; + uint32_t mMaxDepth; + float mConcavePercent; + uint32_t mOutputCount; + uint32_t mOutputPow2; + hacd::ICallback *mICallback; +}; + +static float fm_computePlane(const float *A,const float *B,const float *C,float *n) // returns D +{ + float vx = (B[0] - C[0]); + float vy = (B[1] - C[1]); + float vz = (B[2] - C[2]); + + float wx = (A[0] - B[0]); + float wy = (A[1] - B[1]); + float wz = (A[2] - B[2]); + + float vw_x = vy * wz - vz * wy; + float vw_y = vz * wx - vx * wz; + float vw_z = vx * wy - vy * wx; + + float mag = ::sqrtf((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if ( mag < 0.000001f ) + { + mag = 0; + } + else + { + mag = 1.0f/mag; + } + + float x = vw_x * mag; + float y = vw_y * mag; + float z = vw_z * mag; + + + float D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2])); + + n[0] = x; + n[1] = y; + n[2] = z; + + return D; +} + +static float fm_dot(const float *p1,const float *p2) +{ + return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]; +} + + + +static bool fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon,float dEpsilon,bool doubleSided) +{ + bool ret = false; + + float diff = (float) fabs(p1[3]-p2[3]); + if ( diff < dEpsilon ) // if the plane -d co-efficient is within our epsilon + { + float dot = fm_dot(p1,p2); // compute the dot-product of the vector normals. + if ( doubleSided ) dot = (float)fabs(dot); + float dmin = 1 - normalEpsilon; + float dmax = 1 + normalEpsilon; + if ( dot >= dmin && dot <= dmax ) + { + ret = true; // then the plane equation is for practical purposes identical. + } + } + + return ret; +} + +static bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided) // returns true if this collection of indexed triangles are co-planar! +{ + bool ret = true; + + if ( tcount > 0 ) + { + uint32_t i1 = indices[0]; + uint32_t i2 = indices[1]; + uint32_t i3 = indices[2]; + const float *p1 = &vertices[i1*3]; + const float *p2 = &vertices[i2*3]; + const float *p3 = &vertices[i3*3]; + float plane[4]; + plane[3] = fm_computePlane(p1,p2,p3,plane); + const uint32_t *scan = &indices[3]; + for (uint32_t i=1; i<tcount; i++) + { + i1 = *scan++; + i2 = *scan++; + i3 = *scan++; + p1 = &vertices[i1*3]; + p2 = &vertices[i2*3]; + p3 = &vertices[i3*3]; + float _plane[4]; + _plane[3] = fm_computePlane(p1,p2,p3,_plane); + if ( !fm_samePlane(plane,_plane,0.01f,0.001f,doubleSided) ) + { + ret = false; + break; + } + } + } + return ret; +} + + +static float fm_distToPlane(const float *plane,const float *p) // computes the distance of this point from the plane. +{ + return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3]; +} + +static void fm_cross(float *cross,const float *a,const float *b) +{ + cross[0] = a[1]*b[2] - a[2]*b[1]; + cross[1] = a[2]*b[0] - a[0]*b[2]; + cross[2] = a[0]*b[1] - a[1]*b[0]; +} + +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + + + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +inline float det(const float *p1,const float *p2,const float *p3) +{ + return p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2]; +} + + +static float fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices) +{ + float volume = 0; + + for (uint32_t i=0; i<tcount; i++,indices+=3) + { + const float *p1 = &vertices[ indices[0]*3 ]; + const float *p2 = &vertices[ indices[1]*3 ]; + const float *p3 = &vertices[ indices[2]*3 ]; + volume+=det(p1,p2,p3); // compute the volume of the tetrahedran relative to the origin. + } + + volume*=(1.0f/6.0f); + if ( volume < 0 ) + volume*=-1; + return volume; +} + + + +// assumes that the points are on opposite sides of the plane! +static void fm_intersectPointPlane(const float *p1,const float *p2,float *split,const float *plane) +{ + + float dp1 = fm_distToPlane(plane,p1); + + float dir[3]; + + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + + float dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2]; + float dot2 = dp1 - plane[3]; + + float t = -(plane[3] + dot2 ) / dot1; + + split[0] = (dir[0]*t)+p1[0]; + split[1] = (dir[1]*t)+p1[1]; + split[2] = (dir[2]*t)+p1[2]; + +} + + +static void fm_transform(const float matrix[16],const float v[3],float t[3]) // rotate and translate this point +{ + if ( matrix ) + { + float tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0]; + float ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1]; + float tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2]; + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + +static void fm_rotate(const float matrix[16],const float v[3],float t[3]) // rotate and translate this point +{ + if ( matrix ) + { + float tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]); + float ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]); + float tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]); + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + + + + +static void fm_inverseRT(const float matrix[16],const float pos[3],float t[3]) // inverse rotate translate the point. +{ + + float _x = pos[0] - matrix[3*4+0]; + float _y = pos[1] - matrix[3*4+1]; + float _z = pos[2] - matrix[3*4+2]; + + // Multiply inverse-translated source vector by inverted rotation transform + + t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z); + t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z); + t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z); + +} + +template <class Type> class Eigen +{ +public: + + + void DecrSortEigenStuff(void) + { + Tridiagonal(); //diagonalize the matrix. + QLAlgorithm(); // + DecreasingSort(); + GuaranteeRotation(); + } + + void Tridiagonal(void) + { + Type fM00 = mElement[0][0]; + Type fM01 = mElement[0][1]; + Type fM02 = mElement[0][2]; + Type fM11 = mElement[1][1]; + Type fM12 = mElement[1][2]; + Type fM22 = mElement[2][2]; + + m_afDiag[0] = fM00; + m_afSubd[2] = 0; + if (fM02 != (Type)0.0) + { + Type fLength = static_cast<Type>(::sqrt(fM01*fM01+fM02*fM02)); + Type fInvLength = ((Type)1.0)/fLength; + fM01 *= fInvLength; + fM02 *= fInvLength; + Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11); + m_afDiag[1] = fM11+fM02*fQ; + m_afDiag[2] = fM22-fM02*fQ; + m_afSubd[0] = fLength; + m_afSubd[1] = fM12-fM01*fQ; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = fM01; + mElement[1][2] = fM02; + mElement[2][0] = (Type)0.0; + mElement[2][1] = fM02; + mElement[2][2] = -fM01; + m_bIsRotation = false; + } + else + { + m_afDiag[1] = fM11; + m_afDiag[2] = fM22; + m_afSubd[0] = fM01; + m_afSubd[1] = fM12; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = (Type)1.0; + mElement[1][2] = (Type)0.0; + mElement[2][0] = (Type)0.0; + mElement[2][1] = (Type)0.0; + mElement[2][2] = (Type)1.0; + m_bIsRotation = true; + } + } + + bool QLAlgorithm(void) + { + const int32_t iMaxIter = 32; + + for (int32_t i0 = 0; i0 <3; i0++) + { + int32_t i1; + for (i1 = 0; i1 < iMaxIter; i1++) + { + int32_t i2; + for (i2 = i0; i2 <= (3-2); i2++) + { + Type fTmp = static_cast<Type>(fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1])); + if ( fabs(m_afSubd[i2]) + fTmp == fTmp ) + break; + } + if (i2 == i0) + { + break; + } + + Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]); + Type fR = static_cast<Type>(::sqrt(fG*fG+(Type)1.0)); + if (fG < (Type)0.0) + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR); + } + else + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR); + } + Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0; + for (int32_t i3 = i2-1; i3 >= i0; i3--) + { + Type fF = fSin*m_afSubd[i3]; + Type fB = fCos*m_afSubd[i3]; + if (fabs(fF) >= fabs(fG)) + { + fCos = fG/fF; + fR = static_cast<Type>(::sqrt(fCos*fCos+(Type)1.0)); + m_afSubd[i3+1] = fF*fR; + fSin = ((Type)1.0)/fR; + fCos *= fSin; + } + else + { + fSin = fF/fG; + fR = static_cast<Type>(::sqrt(fSin*fSin+(Type)1.0)); + m_afSubd[i3+1] = fG*fR; + fCos = ((Type)1.0)/fR; + fSin *= fCos; + } + fG = m_afDiag[i3+1]-fP; + fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos; + fP = fSin*fR; + m_afDiag[i3+1] = fG+fP; + fG = fCos*fR-fB; + for (int32_t i4 = 0; i4 < 3; i4++) + { + fF = mElement[i4][i3+1]; + mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF; + mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF; + } + } + m_afDiag[i0] -= fP; + m_afSubd[i0] = fG; + m_afSubd[i2] = (Type)0.0; + } + if (i1 == iMaxIter) + { + return false; + } + } + return true; + } + + void DecreasingSort(void) + { + //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1] + for (int32_t i0 = 0, i1; i0 <= 3-2; i0++) + { + // locate maximum eigenvalue + i1 = i0; + Type fMax = m_afDiag[i1]; + int32_t i2; + for (i2 = i0+1; i2 < 3; i2++) + { + if (m_afDiag[i2] > fMax) + { + i1 = i2; + fMax = m_afDiag[i1]; + } + } + + if (i1 != i0) + { + // swap eigenvalues + m_afDiag[i1] = m_afDiag[i0]; + m_afDiag[i0] = fMax; + // swap eigenvectors + for (i2 = 0; i2 < 3; i2++) + { + Type fTmp = mElement[i2][i0]; + mElement[i2][i0] = mElement[i2][i1]; + mElement[i2][i1] = fTmp; + m_bIsRotation = !m_bIsRotation; + } + } + } + } + + + void GuaranteeRotation(void) + { + if (!m_bIsRotation) + { + // change sign on the first column + for (int32_t iRow = 0; iRow <3; iRow++) + { + mElement[iRow][0] = -mElement[iRow][0]; + } + } + } + + Type mElement[3][3]; + Type m_afDiag[3]; + Type m_afSubd[3]; + bool m_bIsRotation; +}; + + +static bool fm_computeBestFitPlane(uint32_t vcount, + const float *points, + uint32_t vstride, + const float *weights, + uint32_t wstride, + float *plane) +{ + bool ret = false; + + float kOrigin[3] = { 0, 0, 0 }; + + float wtotal = 0; + + { + const char *source = (const char *) points; + const char *wsource = (const char *) weights; + + for (uint32_t i=0; i<vcount; i++) + { + + const float *p = (const float *) source; + + float w = 1; + + if ( wsource ) + { + const float *ws = (const float *) wsource; + w = *ws; // + wsource+=wstride; + } + + kOrigin[0]+=p[0]*w; + kOrigin[1]+=p[1]*w; + kOrigin[2]+=p[2]*w; + + wtotal+=w; + + source+=vstride; + } + } + + float recip = 1.0f / wtotal; // reciprocol of total weighting + + kOrigin[0]*=recip; + kOrigin[1]*=recip; + kOrigin[2]*=recip; + + + float fSumXX=0; + float fSumXY=0; + float fSumXZ=0; + + float fSumYY=0; + float fSumYZ=0; + float fSumZZ=0; + + + { + const char *source = (const char *) points; + const char *wsource = (const char *) weights; + + for (uint32_t i=0; i<vcount; i++) + { + + const float *p = (const float *) source; + + float w = 1; + + if ( wsource ) + { + const float *ws = (const float *) wsource; + w = *ws; // + wsource+=wstride; + } + + float kDiff[3]; + + kDiff[0] = w*(p[0] - kOrigin[0]); // apply vertex weighting! + kDiff[1] = w*(p[1] - kOrigin[1]); + kDiff[2] = w*(p[2] - kOrigin[2]); + + fSumXX+= kDiff[0] * kDiff[0]; // sume of the squares of the differences. + fSumXY+= kDiff[0] * kDiff[1]; // sume of the squares of the differences. + fSumXZ+= kDiff[0] * kDiff[2]; // sume of the squares of the differences. + + fSumYY+= kDiff[1] * kDiff[1]; + fSumYZ+= kDiff[1] * kDiff[2]; + fSumZZ+= kDiff[2] * kDiff[2]; + + + source+=vstride; + } + } + + fSumXX *= recip; + fSumXY *= recip; + fSumXZ *= recip; + fSumYY *= recip; + fSumYZ *= recip; + fSumZZ *= recip; + + // setup the eigensolver + Eigen<float> kES; + + kES.mElement[0][0] = fSumXX; + kES.mElement[0][1] = fSumXY; + kES.mElement[0][2] = fSumXZ; + + kES.mElement[1][0] = fSumXY; + kES.mElement[1][1] = fSumYY; + kES.mElement[1][2] = fSumYZ; + + kES.mElement[2][0] = fSumXZ; + kES.mElement[2][1] = fSumYZ; + kES.mElement[2][2] = fSumZZ; + + // compute eigenstuff, smallest eigenvalue is in last position + kES.DecrSortEigenStuff(); + + float kNormal[3]; + + kNormal[0] = kES.mElement[0][2]; + kNormal[1] = kES.mElement[1][2]; + kNormal[2] = kES.mElement[2][2]; + + // the minimum energy + plane[0] = kNormal[0]; + plane[1] = kNormal[1]; + plane[2] = kNormal[2]; + + plane[3] = 0 - fm_dot(kNormal,kOrigin); + + ret = true; + + return ret; +} + + + + +// computes the OBB for this set of points relative to this transform matrix. +void computeOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float *matrix) +{ + const char *src = (const char *) points; + + float bmin[3] = { 1e9, 1e9, 1e9 }; + float bmax[3] = { -1e9, -1e9, -1e9 }; + + for (uint32_t i=0; i<vcount; i++) + { + const float *p = (const float *) src; + float t[3]; + + fm_inverseRT(matrix, p, t ); // inverse rotate translate + + if ( t[0] < bmin[0] ) bmin[0] = t[0]; + if ( t[1] < bmin[1] ) bmin[1] = t[1]; + if ( t[2] < bmin[2] ) bmin[2] = t[2]; + + if ( t[0] > bmax[0] ) bmax[0] = t[0]; + if ( t[1] > bmax[1] ) bmax[1] = t[1]; + if ( t[2] > bmax[2] ) bmax[2] = t[2]; + + src+=pstride; + } + + float center[3]; + + sides[0] = bmax[0]-bmin[0]; + sides[1] = bmax[1]-bmin[1]; + sides[2] = bmax[2]-bmin[2]; + + center[0] = sides[0]*0.5f+bmin[0]; + center[1] = sides[1]*0.5f+bmin[1]; + center[2] = sides[2]*0.5f+bmin[2]; + + float ocenter[3]; + + fm_rotate(matrix,center,ocenter); + + matrix[12]+=ocenter[0]; + matrix[13]+=ocenter[1]; + matrix[14]+=ocenter[2]; + +} + +// Reference, from Stan Melax in Game Gems I +// Quaternion q; +// vector3 c = CrossProduct(v0,v1); +// float d = DotProduct(v0,v1); +// float s = (float)::sqrt((1+d)*2); +// q.x = c.x / s; +// q.y = c.y / s; +// q.z = c.z / s; +// q.w = s /2.0f; +// return q; +static void fm_rotationArc(const float *v0,const float *v1,float *quat) +{ + float cross[3]; + + fm_cross(cross,v0,v1); + float d = fm_dot(v0,v1); + + if(d<=-1.0f) // 180 about x axis + { + quat[0] = 1.0f; + quat[1] = quat[2] = quat[3] =0.0f; + return; + } + else + { + float s = ::sqrtf((1+d)*2); + float recip = 1.0f / s; + + quat[0] = cross[0] * recip; + quat[1] = cross[1] * recip; + quat[2] = cross[2] * recip; + quat[3] = s * 0.5f; + } +} + +static void fm_setTranslation(const float *translation,float *matrix) +{ + matrix[12] = translation[0]; + matrix[13] = translation[1]; + matrix[14] = translation[2]; +} + + +static void fm_eulerToQuat(float roll,float pitch,float yaw,float *quat) // convert euler angles to quaternion. +{ + roll *= 0.5f; + pitch *= 0.5f; + yaw *= 0.5f; + + float cr = ::cosf(roll); + float cp = ::cosf(pitch); + float cy = ::cosf(yaw); + + float sr = ::sinf(roll); + float sp = ::sinf(pitch); + float sy = ::sinf(yaw); + + float cpcy = cp * cy; + float spsy = sp * sy; + float spcy = sp * cy; + float cpsy = cp * sy; + + quat[0] = ( sr * cpcy - cr * spsy); + quat[1] = ( cr * spcy + sr * cpsy); + quat[2] = ( cr * cpsy - sr * spcy); + quat[3] = cr * cpcy + sr * spsy; +} + + +static void fm_quatToMatrix(const float *quat,float *matrix) // convert quaterinion rotation to matrix, zeros out the translation component. +{ + + float xx = quat[0]*quat[0]; + float yy = quat[1]*quat[1]; + float zz = quat[2]*quat[2]; + float xy = quat[0]*quat[1]; + float xz = quat[0]*quat[2]; + float yz = quat[1]*quat[2]; + float wx = quat[3]*quat[0]; + float wy = quat[3]*quat[1]; + float wz = quat[3]*quat[2]; + + matrix[0*4+0] = 1 - 2 * ( yy + zz ); + matrix[1*4+0] = 2 * ( xy - wz ); + matrix[2*4+0] = 2 * ( xz + wy ); + + matrix[0*4+1] = 2 * ( xy + wz ); + matrix[1*4+1] = 1 - 2 * ( xx + zz ); + matrix[2*4+1] = 2 * ( yz - wx ); + + matrix[0*4+2] = 2 * ( xz - wy ); + matrix[1*4+2] = 2 * ( yz + wx ); + matrix[2*4+2] = 1 - 2 * ( xx + yy ); + + matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (float) 0.0f; + matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (float) 0.0f; + matrix[3*4+3] =(float) 1.0f; + +} + +static void fm_matrixMultiply(const float *pA,const float *pB,float *pM) +{ + float a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; + float b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; + float c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; + float d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; + + float e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; + float f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; + float g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; + float h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; + + float i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; + float j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; + float k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; + float l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; + + float m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; + float n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; + float o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; + float p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; + + pM[0] = a; + pM[1] = b; + pM[2] = c; + pM[3] = d; + + pM[4] = e; + pM[5] = f; + pM[6] = g; + pM[7] = h; + + pM[8] = i; + pM[9] = j; + pM[10] = k; + pM[11] = l; + + pM[12] = m; + pM[13] = n; + pM[14] = o; + pM[15] = p; +} + + + + +static void fm_planeToMatrix(const float *plane,float *matrix) // convert a plane equation to a 4x4 rotation matrix +{ + float ref[3] = { 0, 1, 0 }; + float quat[4]; + fm_rotationArc(ref,plane,quat); + fm_quatToMatrix(quat,matrix); + float origin[3] = { 0, -plane[3], 0 }; + float center[3]; + fm_transform(matrix,origin,center); + fm_setTranslation(center,matrix); +} + + +static void fm_computeBestFitOBB(uint32_t vcount, + const float *points, + uint32_t pstride, + float *sides, + float *matrix, + bool bruteForce) +{ + float plane[4]; + fm_computeBestFitPlane(vcount,points,pstride,0,0,plane); + fm_planeToMatrix(plane,matrix); + computeOBB( vcount, points, pstride, sides, matrix ); + + float refmatrix[16]; + memcpy(refmatrix,matrix,16*sizeof(float)); + + float volume = sides[0]*sides[1]*sides[2]; + if ( bruteForce ) + { + for (float a=10; a<180; a+=10) + { + float quat[4]; + fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat); + float temp[16]; + float pmatrix[16]; + fm_quatToMatrix(quat,temp); + fm_matrixMultiply(temp,refmatrix,pmatrix); + float psides[3]; + computeOBB( vcount, points, pstride, psides, pmatrix ); + float v = psides[0]*psides[1]*psides[2]; + if ( v < volume ) + { + volume = v; + memcpy(matrix,pmatrix,sizeof(float)*16); + sides[0] = psides[0]; + sides[1] = psides[1]; + sides[2] = psides[2]; + } + } + } +} + +template <class T> class Rect3d +{ +public: + Rect3d(void) { }; + + Rect3d(const T *bmin,const T *bmax) + { + + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + + } + + void SetMin(const T *bmin) + { + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + } + + void SetMax(const T *bmax) + { + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + } + + void SetMin(T x,T y,T z) + { + mMin[0] = x; + mMin[1] = y; + mMin[2] = z; + } + + void SetMax(T x,T y,T z) + { + mMax[0] = x; + mMax[1] = y; + mMax[2] = z; + } + + T mMin[3]; + T mMax[3]; +}; + +void splitRect(uint32_t axis, + const Rect3d<float> &source, + Rect3d<float> &b1, + Rect3d<float> &b2, + const float *midpoint) +{ + switch ( axis ) + { + case 0: + b1.SetMin(source.mMin); + b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] ); + + b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 1: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] ); + + b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 2: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] ); + + b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] ); + b2.SetMax(source.mMax); + + break; + } +} + + + +static bool fm_computeSplitPlane(uint32_t vcount, + const float *vertices, + uint32_t /* tcount */, + const uint32_t * /* indices */, + float *plane) +{ + + float sides[3]; + float matrix[16]; + + fm_computeBestFitOBB( vcount, vertices, sizeof(float)*3, sides, matrix, false ); + + float bmax[3]; + float bmin[3]; + + bmax[0] = sides[0]*0.5f; + bmax[1] = sides[1]*0.5f; + bmax[2] = sides[2]*0.5f; + + bmin[0] = -bmax[0]; + bmin[1] = -bmax[1]; + bmin[2] = -bmax[2]; + + + float dx = sides[0]; + float dy = sides[1]; + float dz = sides[2]; + + + uint32_t axis = 0; + + if ( dy > dx ) + { + axis = 1; + } + + if ( dz > dx && dz > dy ) + { + axis = 2; + } + + float p1[3]; + float p2[3]; + float p3[3]; + + p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f; + p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f; + p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f; + + Rect3d<float> b(bmin,bmax); + + Rect3d<float> b1,b2; + + splitRect(axis,b,b1,b2,p1); + + + switch ( axis ) + { + case 0: + p2[1] = bmin[1]; + p2[2] = bmin[2]; + + if ( dz > dy ) + { + p3[1] = bmax[1]; + p3[2] = bmin[2]; + } + else + { + p3[1] = bmin[1]; + p3[2] = bmax[2]; + } + + break; + case 1: + p2[0] = bmin[0]; + p2[2] = bmin[2]; + + if ( dx > dz ) + { + p3[0] = bmax[0]; + p3[2] = bmin[2]; + } + else + { + p3[0] = bmin[0]; + p3[2] = bmax[2]; + } + + break; + case 2: + p2[0] = bmin[0]; + p2[1] = bmin[1]; + + if ( dx > dy ) + { + p3[0] = bmax[0]; + p3[1] = bmin[1]; + } + else + { + p3[0] = bmin[0]; + p3[1] = bmax[1]; + } + + break; + } + + float tp1[3]; + float tp2[3]; + float tp3[3]; + + fm_transform(matrix,p1,tp1); + fm_transform(matrix,p2,tp2); + fm_transform(matrix,p3,tp3); + + plane[3] = fm_computePlane(tp1,tp2,tp3,plane); + + return true; + +} + + + +#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity + +class fm_VertexIndex +{ +public: + virtual uint32_t getIndex(const float pos[3],bool &newPos) = 0; // get welded index for this float vector[3] + virtual uint32_t getIndex(const double pos[3],bool &newPos) = 0; // get welded index for this double vector[3] + virtual const float * getVerticesFloat(void) const = 0; + virtual const double * getVerticesDouble(void) const = 0; + virtual const float * getVertexFloat(uint32_t index) const = 0; + virtual const double * getVertexDouble(uint32_t index) const = 0; + virtual uint32_t getVcount(void) const = 0; + virtual bool isDouble(void) const = 0; + virtual bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0; +}; + +//static fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles +static void fm_releaseVertexIndex(fm_VertexIndex *vindex); + + + +class KdTreeNode; + +typedef hacd::vector< KdTreeNode * > KdTreeNodeVector; + +enum Axes +{ + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 +}; + +class KdTreeFindNode +{ +public: + KdTreeFindNode(void) + { + mNode = 0; + mDistance = 0; + } + KdTreeNode *mNode; + double mDistance; +}; + +class KdTreeInterface +{ +public: + virtual const double * getPositionDouble(uint32_t index) const = 0; + virtual const float * getPositionFloat(uint32_t index) const = 0; +}; + +class KdTreeNode +{ +public: + KdTreeNode(void) + { + mIndex = 0; + mLeft = 0; + mRight = 0; + } + + KdTreeNode(uint32_t index) + { + mIndex = index; + mLeft = 0; + mRight = 0; + }; + + ~KdTreeNode(void) + { + } + + + void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const double *nodePosition = iface->getPositionDouble( node->mIndex ); + const double *position = iface->getPositionDouble( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addDouble(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addDouble(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addDouble(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const float *nodePosition = iface->getPositionFloat( node->mIndex ); + const float *position = iface->getPositionFloat( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addFloat(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addFloat(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addFloat(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + uint32_t getIndex(void) const { return mIndex; }; + + void search(Axes axis,const double *pos,double radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const double *position = iface->getPositionDouble(mIndex); + + double dx = pos[0] - position[0]; + double dy = pos[1] - position[1]; + double dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + double r2 = radius*radius; + double m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (uint32_t i=0; i<count; i++) + { + if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one... + { + // insertion sort... + uint32_t scan = count; + if ( scan >= maxObjects ) scan=maxObjects-1; + for (uint32_t j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + + void search(Axes axis,const float *pos,float radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const float *position = iface->getPositionFloat(mIndex); + + float dx = pos[0] - position[0]; + float dy = pos[1] - position[1]; + float dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + float r2 = radius*radius; + float m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (uint32_t i=0; i<count; i++) + { + if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one... + { + // insertion sort... + uint32_t scan = count; + if ( scan >= maxObjects ) scan=maxObjects-1; + for (uint32_t j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + +private: + + void setLeft(KdTreeNode *left) { mLeft = left; }; + void setRight(KdTreeNode *right) { mRight = right; }; + + KdTreeNode *getLeft(void) { return mLeft; } + KdTreeNode *getRight(void) { return mRight; } + + uint32_t mIndex; + KdTreeNode *mLeft; + KdTreeNode *mRight; +}; + + +#define MAX_BUNDLE_SIZE 1024 // 1024 nodes at a time, to minimize memory allocation and guarentee that pointers are persistent. + +class KdTreeNodeBundle : public UANS::UserAllocated +{ +public: + + KdTreeNodeBundle(void) + { + mNext = 0; + mIndex = 0; + } + + bool isFull(void) const + { + return (bool)( mIndex == MAX_BUNDLE_SIZE ); + } + + KdTreeNode * getNextNode(void) + { + assert(mIndex<MAX_BUNDLE_SIZE); + KdTreeNode *ret = &mNodes[mIndex]; + mIndex++; + return ret; + } + + KdTreeNodeBundle *mNext; + uint32_t mIndex; + KdTreeNode mNodes[MAX_BUNDLE_SIZE]; +}; + + +typedef hacd::vector< double > DoubleVector; +typedef hacd::vector< float > FloatVector; + +class KdTree : public KdTreeInterface, public UANS::UserAllocated +{ +public: + KdTree(void) + { + mRoot = 0; + mBundle = 0; + mVcount = 0; + mUseDouble = false; + } + + virtual ~KdTree(void) + { + reset(); + } + + const double * getPositionDouble(uint32_t index) const + { + assert( mUseDouble ); + assert ( index < mVcount ); + return &mVerticesDouble[index*3]; + } + + const float * getPositionFloat(uint32_t index) const + { + assert( !mUseDouble ); + assert ( index < mVcount ); + return &mVerticesFloat[index*3]; + } + + uint32_t search(const double *pos,double radius,uint32_t maxObjects,KdTreeFindNode *found) const + { + assert( mUseDouble ); + if ( !mRoot ) return 0; + uint32_t count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + uint32_t search(const float *pos,float radius,uint32_t maxObjects,KdTreeFindNode *found) const + { + assert( !mUseDouble ); + if ( !mRoot ) return 0; + uint32_t count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + void reset(void) + { + mRoot = 0; + mVerticesDouble.clear(); + mVerticesFloat.clear(); + KdTreeNodeBundle *bundle = mBundle; + while ( bundle ) + { + KdTreeNodeBundle *next = bundle->mNext; + delete bundle; + bundle = next; + } + mBundle = 0; + mVcount = 0; + } + + uint32_t add(double x,double y,double z) + { + assert(mUseDouble); + uint32_t ret = mVcount; + mVerticesDouble.push_back(x); + mVerticesDouble.push_back(y); + mVerticesDouble.push_back(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addDouble(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + uint32_t add(float x,float y,float z) + { + assert(!mUseDouble); + uint32_t ret = mVcount; + mVerticesFloat.push_back(x); + mVerticesFloat.push_back(y); + mVerticesFloat.push_back(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addFloat(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + KdTreeNode * getNewNode(uint32_t index) + { + if ( mBundle == 0 ) + { + mBundle = HACD_NEW(KdTreeNodeBundle); + } + if ( mBundle->isFull() ) + { + KdTreeNodeBundle *bundle = HACD_NEW(KdTreeNodeBundle); + mBundle->mNext = bundle; + mBundle = bundle; + } + KdTreeNode *node = mBundle->getNextNode(); + new ( node ) KdTreeNode(index); + return node; + } + + uint32_t getNearest(const double *pos,double radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( mUseDouble ); + uint32_t ret = 0; + + _found = false; + KdTreeFindNode found[1]; + uint32_t count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + uint32_t getNearest(const float *pos,float radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( !mUseDouble ); + uint32_t ret = 0; + + _found = false; + KdTreeFindNode found[1]; + uint32_t count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + const double * getVerticesDouble(void) const + { + assert( mUseDouble ); + const double *ret = 0; + if ( !mVerticesDouble.empty() ) + { + ret = &mVerticesDouble[0]; + } + return ret; + } + + const float * getVerticesFloat(void) const + { + assert( !mUseDouble ); + const float * ret = 0; + if ( !mVerticesFloat.empty() ) + { + ret = &mVerticesFloat[0]; + } + return ret; + } + + uint32_t getVcount(void) const { return mVcount; }; + + void setUseDouble(bool useDouble) + { + mUseDouble = useDouble; + } + +private: + bool mUseDouble; + KdTreeNode *mRoot; + KdTreeNodeBundle *mBundle; + uint32_t mVcount; + DoubleVector mVerticesDouble; + FloatVector mVerticesFloat; +}; + +class MyVertexIndex : public fm_VertexIndex, public UANS::UserAllocated +{ +public: + MyVertexIndex(double granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (float)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = true; + mKdTree.setUseDouble(true); + } + + MyVertexIndex(float granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (float)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = false; + mKdTree.setUseDouble(false); + } + + virtual ~MyVertexIndex(void) + { + + } + + + double snapToGrid(double p) + { + double m = fmod(p,mDoubleGranularity); + p-=m; + return p; + } + + float snapToGrid(float p) + { + float m = fmodf(p,mFloatGranularity); + p-=m; + return p; + } + + uint32_t getIndex(const float *_p,bool &newPos) // get index for a vector float + { + uint32_t ret; + + if ( mUseDouble ) + { + double p[3]; + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + float p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mFloatGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + uint32_t getIndex(const double *_p,bool &newPos) // get index for a vector double + { + uint32_t ret; + + if ( !mUseDouble ) + { + float p[3]; + p[0] = (float)_p[0]; + p[1] = (float)_p[1]; + p[2] = (float)_p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + double p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mDoubleGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + const float * getVerticesFloat(void) const + { + const float * ret = 0; + + assert( !mUseDouble ); + + ret = mKdTree.getVerticesFloat(); + + return ret; + } + + const double * getVerticesDouble(void) const + { + const double * ret = 0; + + assert( mUseDouble ); + + ret = mKdTree.getVerticesDouble(); + + return ret; + } + + const float * getVertexFloat(uint32_t index) const + { + const float * ret = 0; + assert( !mUseDouble ); +#ifdef _DEBUG + uint32_t vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesFloat(); + ret = &ret[index*3]; + return ret; + } + + const double * getVertexDouble(uint32_t index) const + { + const double * ret = 0; + assert( mUseDouble ); +#ifdef _DEBUG + uint32_t vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesDouble(); + ret = &ret[index*3]; + + return ret; + } + + uint32_t getVcount(void) const + { + return mKdTree.getVcount(); + } + + bool isDouble(void) const + { + return mUseDouble; + } + + + bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) + { + bool ret = false; + + + FILE *fph = fopen(fname,"wb"); + if ( fph ) + { + ret = true; + + uint32_t vcount = getVcount(); + if ( mUseDouble ) + { + const double *v = getVerticesDouble(); + for (uint32_t i=0; i<vcount; i++) + { + fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", (float)v[0], (float)v[1], (float)v[2] ); + v+=3; + } + } + else + { + const float *v = getVerticesFloat(); + for (uint32_t i=0; i<vcount; i++) + { + fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", v[0], v[1], v[2] ); + v+=3; + } + } + + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = *indices++; + uint32_t i2 = *indices++; + uint32_t i3 = *indices++; + fprintf(fph,"f %d %d %d\r\n", i1+1, i2+1, i3+1 ); + } + fclose(fph); + } + + return ret; + } + +private: + bool mUseDouble:1; + bool mSnapToGrid:1; + double mDoubleGranularity; + float mFloatGranularity; + KdTree mKdTree; +}; + +static fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid) // create an indexed vertext system for floats +{ + MyVertexIndex *ret = HACD_NEW(MyVertexIndex)(granularity,snapToGrid); + return static_cast< fm_VertexIndex *>(ret); +} + +static void fm_releaseVertexIndex(fm_VertexIndex *vindex) +{ + MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex); + delete m; +} + + +// + +// Split Mesh + +class SimpleMesh +{ +public: + SimpleMesh(void) + { + mVcount = 0; + mTcount = 0; + mVertices = NULL; + mIndices = NULL; + } + SimpleMesh(uint32_t vcount,uint32_t tcount,const float *vertices,const uint32_t *indices) + { + mVcount = 0; + mTcount = 0; + mVertices = NULL; + mIndices = NULL; + set(vcount,tcount,vertices,indices); + } + + void set(uint32_t vcount,uint32_t tcount,const float *vertices,const uint32_t *indices) + { + release(); + mVcount = vcount; + mTcount = tcount; + mVertices = (float *)HACD_ALLOC(sizeof(float)*3*mVcount); + memcpy(mVertices,vertices,sizeof(float)*3*mVcount); + mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*3*mTcount); + memcpy(mIndices,indices,sizeof(uint32_t)*3*mTcount); + } + + + ~SimpleMesh(void) + { + release(); + } + + void release(void) + { + HACD_FREE(mVertices); + HACD_FREE(mIndices); + mVertices = NULL; + mIndices = NULL; + mVcount = 0; + mTcount = 0; + } + + + uint32_t mVcount; + uint32_t mTcount; + float *mVertices; + uint32_t *mIndices; +}; + + +void splitMesh(const float *planeEquation,const SimpleMesh &input,SimpleMesh &left,SimpleMesh &right,bool closedMesh); + +static void addTri(const float *p1, + const float *p2, + const float *p3, + hacd::vector< uint32_t > &indices, + fm_VertexIndex *vertices) +{ + bool newPos; + + uint32_t i1 = vertices->getIndex(p1,newPos); + uint32_t i2 = vertices->getIndex(p2,newPos); + uint32_t i3 = vertices->getIndex(p3,newPos); + + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); +} + +enum PlaneTriResult +{ + PTR_ON_PLANE, + PTR_FRONT, + PTR_BACK, + PTR_SPLIT, +}; + +static PlaneTriResult fm_getSidePlane(const float *p,const float *plane,float epsilon) +{ + PlaneTriResult ret = PTR_ON_PLANE; + + float d = fm_distToPlane(plane,p); + + if ( d < -epsilon || d > epsilon ) + { + if ( d > 0 ) + ret = PTR_FRONT; // it is 'in front' within the provided epsilon value. + else + ret = PTR_BACK; + } + + return ret; +} + + + + +static PlaneTriResult fm_planeTriIntersection(const float plane[4], // the plane equation in Ax+By+Cz+D format + const float *triangle, // the source triangle. + uint32_t tstride, // stride in bytes of the input and output *vertices* + float epsilon, // the co-planer epsilon value. + float *front, // the triangle in front of the + uint32_t &fcount, // number of vertices in the 'front' triangle + float *back, // the triangle in back of the plane + uint32_t &bcount); // the number of vertices in the 'back' triangle. + +static inline void add(const float *p,float *dest,uint32_t tstride,uint32_t &pcount) +{ + char *d = (char *) dest; + d = d + pcount*tstride; + dest = (float *) d; + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; + pcount++; + HACD_ASSERT( pcount <= 4 ); +} + +#define MAXPTS 256 + +template <class Type> class point +{ +public: + + void set(const Type *p) + { + x = p[0]; + y = p[1]; + z = p[2]; + } + + Type x; + Type y; + Type z; +}; + +template <class Type> class plane +{ +public: + plane(const Type *p) + { + normal.x = p[0]; + normal.y = p[1]; + normal.z = p[2]; + D = p[3]; + } + + Type Classify_Point(const point<Type> &p) + { + return p.x*normal.x + p.y*normal.y + p.z*normal.z + D; + } + + point<Type> normal; + Type D; +}; + +template <class Type> class polygon +{ +public: + polygon(void) + { + mVcount = 0; + } + + polygon(const Type *p1,const Type *p2,const Type *p3) + { + mVcount = 3; + mVertices[0].set(p1); + mVertices[1].set(p2); + mVertices[2].set(p3); + } + + + int32_t NumVertices(void) const { return mVcount; }; + + const point<Type>& Vertex(int32_t index) + { + if ( index < 0 ) index+=mVcount; + return mVertices[index]; + }; + + + void set(const point<Type> *pts,int32_t count) + { + for (int32_t i=0; i<count; i++) + { + mVertices[i] = pts[i]; + } + mVcount = count; + } + + + void Split_Polygon(polygon<Type> *poly,plane<Type> *part, polygon<Type> &front, polygon<Type> &back) + { + int32_t count = poly->NumVertices (); + int32_t out_c = 0, in_c = 0; + point<Type> ptA, ptB,outpts[MAXPTS],inpts[MAXPTS]; + Type sideA, sideB; + ptA = poly->Vertex (count - 1); + sideA = part->Classify_Point (ptA); + for (int32_t i = -1; ++i < count;) + { + ptB = poly->Vertex(i); + sideB = part->Classify_Point(ptB); + if (sideB > 0) + { + if (sideA < 0) + { + point<Type> v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + outpts[out_c++] = ptB; + } + else if (sideB < 0) + { + if (sideA > 0) + { + point<Type> v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + inpts[in_c++] = ptB; + } + else + outpts[out_c++] = inpts[in_c++] = ptB; + ptA = ptB; + sideA = sideB; + } + + front.set(&outpts[0], out_c); + back.set(&inpts[0], in_c); + } + + int32_t mVcount; + point<Type> mVertices[MAXPTS]; +}; +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + + + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +static bool fm_rayIntersectsTriangle(const float *p,const float *d,const float *v0,const float *v1,const float *v2,float &t) +{ + float e1[3],e2[3],h[3],s[3],q[3]; + float a,f,u,v; + + vector(e1,v1,v0); + vector(e2,v2,v0); + crossProduct(h,d,e2); + a = innerProduct(e1,h); + + if (a > -0.00001 && a < 0.00001) + return(false); + + f = 1/a; + vector(s,p,v0); + u = f * (innerProduct(s,h)); + + if (u < 0.0 || u > 1.0) + return(false); + + crossProduct(q,s,e1); + v = f * innerProduct(d,q); + if (v < 0.0 || u + v > 1.0) + return(false); + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * innerProduct(e2,q); + if (t > 0) // ray intersection + return(true); + else // this means that there is a line intersection + // but not a ray intersection + return (false); +} + + + +static PlaneTriResult fm_planeTriIntersection(const float *_plane, // the plane equation in Ax+By+Cz+D format + const float *triangle, // the source triangle. + uint32_t tstride, // stride in bytes of the input and output *vertices* + float epsilon, // the co-planar epsilon value. + float *front, // the triangle in front of the + uint32_t &fcount, // number of vertices in the 'front' triangle + float *back, // the triangle in back of the plane + uint32_t &bcount) // the number of vertices in the 'back' triangle. +{ + + fcount = 0; + bcount = 0; + + const char *tsource = (const char *) triangle; + + // get the three vertices of the triangle. + const float *p1 = (const float *) (tsource); + const float *p2 = (const float *) (tsource+tstride); + const float *p3 = (const float *) (tsource+tstride*2); + + + PlaneTriResult r1 = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on + PlaneTriResult r2 = fm_getSidePlane(p2,_plane,epsilon); + PlaneTriResult r3 = fm_getSidePlane(p3,_plane,epsilon); + + // If any of the points lay right *on* the plane.... + if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE ) + { + // If the triangle is completely co-planar, then just treat it as 'front' and return! + if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE ) + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + return PTR_FRONT; + } + // Decide to place the co-planar points on the same side as the co-planar point. + PlaneTriResult r= PTR_ON_PLANE; + if ( r1 != PTR_ON_PLANE ) + r = r1; + else if ( r2 != PTR_ON_PLANE ) + r = r2; + else if ( r3 != PTR_ON_PLANE ) + r = r3; + + if ( r1 == PTR_ON_PLANE ) r1 = r; + if ( r2 == PTR_ON_PLANE ) r2 = r; + if ( r3 == PTR_ON_PLANE ) r3 = r; + + } + + if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. + { + if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + } + else + { + add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle. + add(p2,back,tstride,bcount); + add(p3,back,tstride,bcount); + } + return r1; // if all three points are on the same side of the plane return result + } + + + polygon<float> pi(p1,p2,p3); + polygon<float> pfront,pback; + + plane<float> part(_plane); + + pi.Split_Polygon(&pi,&part,pfront,pback); + + for (int32_t i=0; i<pfront.mVcount; i++) + { + add( &pfront.mVertices[i].x, front, tstride, fcount ); + } + + for (int32_t i=0; i<pback.mVcount; i++) + { + add( &pback.mVertices[i].x, back, tstride, bcount ); + } + + PlaneTriResult ret = PTR_SPLIT; + + if ( fcount < 3 ) fcount = 0; + if ( bcount < 3 ) bcount = 0; + + if ( fcount == 0 && bcount ) + ret = PTR_BACK; + + if ( bcount == 0 && fcount ) + ret = PTR_FRONT; + + + return ret; +} + + + +void splitMesh(const float *planeEquation,const SimpleMesh &input,SimpleMesh &leftMesh,SimpleMesh &rightMesh) +{ + hacd::vector< uint32_t > leftIndices; + hacd::vector< uint32_t > rightIndices; + + fm_VertexIndex *leftVertices = fm_createVertexIndex(0.00001f,false); + fm_VertexIndex *rightVertices = fm_createVertexIndex(0.00001f,false); + + { + for (uint32_t i=0; i<input.mTcount; i++) + { + uint32_t i1 = input.mIndices[i*3+0]; + uint32_t i2 = input.mIndices[i*3+1]; + uint32_t i3 = input.mIndices[i*3+2]; + + float *p1 = &input.mVertices[i1*3]; + float *p2 = &input.mVertices[i2*3]; + float *p3 = &input.mVertices[i3*3]; + + float tri[3*3]; + + tri[0] = p1[0]; + tri[1] = p1[1]; + tri[2] = p1[2]; + + tri[3] = p2[0]; + tri[4] = p2[1]; + tri[5] = p2[2]; + + tri[6] = p3[0]; + tri[7] = p3[1]; + tri[8] = p3[2]; + + float front[3*5]; + float back[3*5]; + + uint32_t fcount,bcount; + + PlaneTriResult result = fm_planeTriIntersection(planeEquation,tri,sizeof(float)*3,0.00001f,front,fcount,back,bcount); + + switch ( result ) + { + case PTR_FRONT: + addTri(p1,p2,p3,leftIndices,leftVertices); + break; + case PTR_BACK: + addTri(p1,p2,p3,rightIndices,rightVertices); + break; + case PTR_SPLIT: + if ( fcount ) + { + addTri(&front[0],&front[3],&front[6],leftIndices,leftVertices); + if ( fcount == 4 ) + { + addTri(&front[0],&front[6],&front[9],leftIndices,leftVertices); + } + } + if ( bcount ) + { + addTri(&back[0],&back[3],&back[6],rightIndices,rightVertices); + if ( bcount == 4 ) + { + addTri(&back[0],&back[6],&back[9],rightIndices,rightVertices); + } + } + break; + case PTR_ON_PLANE: // Make compiler happy + break; + } + } + } + + if ( !leftIndices.empty() ) + { + leftMesh.set(leftVertices->getVcount(),leftIndices.size()/3,leftVertices->getVerticesFloat(),&leftIndices[0]); + } + + if ( !rightIndices.empty() ) + { + rightMesh.set(rightVertices->getVcount(),rightIndices.size()/3,rightVertices->getVerticesFloat(),&rightIndices[0]); + } + fm_releaseVertexIndex(leftVertices); + fm_releaseVertexIndex(rightVertices); +} + + +// + +static float enorm0_3d ( float x0, float y0, float z0, float x1, float y1, float z1 ) + +/**********************************************************************/ + +/* +Purpose: + +ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D. + +Modified: + +18 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, float X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points +P0 and P1. + +Output, float ENORM0_3D, the Euclidean norm of (P1-P0). +*/ +{ + float value; + + value = ::sqrtf ( + ( x1 - x0 ) * ( x1 - x0 ) + + ( y1 - y0 ) * ( y1 - y0 ) + + ( z1 - z0 ) * ( z1 - z0 ) ); + + return value; +} + + +static float triangle_area_3d ( float x1, float y1, float z1, float x2,float y2, float z2, float x3, float y3, float z3 ) + + /**********************************************************************/ + + /* + Purpose: + + TRIANGLE_AREA_3D computes the area of a triangle in 3D. + + Modified: + + 22 April 1999 + + Author: + + John Burkardt + + Parameters: + + Input, float X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z) + coordinates of the corners of the triangle. + + Output, float TRIANGLE_AREA_3D, the area of the triangle. + */ +{ + float a; + float alpha; + float area; + float b; + float base; + float c; + float dot; + float height; + /* + Find the projection of (P3-P1) onto (P2-P1). + */ + dot = + ( x2 - x1 ) * ( x3 - x1 ) + + ( y2 - y1 ) * ( y3 - y1 ) + + ( z2 - z1 ) * ( z3 - z1 ); + + base = enorm0_3d ( x1, y1, z1, x2, y2, z2 ); + /* + The height of the triangle is the length of (P3-P1) after its + projection onto (P2-P1) has been subtracted. + */ + if ( base == 0.0 ) { + + height = 0.0; + + } + else { + + alpha = dot / ( base * base ); + + a = x3 - x1 - alpha * ( x2 - x1 ); + b = y3 - y1 - alpha * ( y2 - y1 ); + c = z3 - z1 - alpha * ( z2 - z1 ); + + height = ::sqrtf ( a * a + b * b + c * c ); + + } + + area = 0.5f * base * height; + + return area; +} + + +float fm_computeArea(const float *p1,const float *p2,const float *p3) +{ + float ret = 0; + + ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]); + + return ret; +} + +void validate(float v) +{ + HACD_UNUSED(v); + HACD_ASSERT( v >= -1000 && v < 1000 ); +} + +void validate(const float *v) +{ + validate(v[0]); + validate(v[1]); + validate(v[2]); +} + + +void addVertex(const float *p,float *dest,uint32_t index) +{ + dest[index*3+0] = p[0]; + dest[index*3+1] = p[1]; + dest[index*3+2] = p[2]; + + validate( &dest[index*3]); + +} + +void addTriangle(uint32_t *indices,uint32_t i1,uint32_t i2,uint32_t i3,uint32_t index) +{ + indices[index*3+0] = i1; + indices[index*3+1] = i2; + indices[index*3+2] = i3; +} + +bool projectRay(const float *p,const float *n,float *t,const HACD::HullResult &hull) +{ + bool ret = false; + + t[0] = p[0]; + t[1] = p[1]; + t[2] = p[2]; + validate(p); + validate(n); + + for (uint32_t i=0; i<hull.mNumTriangles; i++) + { + uint32_t i1 = hull.mIndices[i*3+0]; + uint32_t i2 = hull.mIndices[i*3+1]; + uint32_t i3 = hull.mIndices[i*3+2]; + + const float *p1 = &hull.mOutputVertices[i1*3]; + const float *p2 = &hull.mOutputVertices[i2*3]; + const float *p3 = &hull.mOutputVertices[i3*3]; + + float tm; + if ( fm_rayIntersectsTriangle(p,n,p1,p2,p3,tm)) + { + if ( tm > 100 ) + { + fm_rayIntersectsTriangle(p,n,p1,p2,p3,tm); + } + t[0] = p[0]+n[0]*tm; + t[1] = p[1]+n[1]*tm; + t[2] = p[2]+n[2]*tm; + ret = true; + break; + } + } + + if ( ret ) + { + validate(t); + } + + return ret; +} + +float computeProjectedVolume(const float *p1,const float *p2,const float *p3,const HACD::HullResult &hull) +{ + float ret = 0; + + float area = fm_computeArea(p1,p2,p3); + if ( area <= 0 ) + { + return 0; + } + + float normal[3]; + fm_computePlane(p3,p2,p1,normal); + + float t1[3]; + float t2[3]; + float t3[3]; + + bool hit1 = projectRay(p1,normal,t1,hull); + bool hit2 = projectRay(p2,normal,t2,hull); + bool hit3 = projectRay(p3,normal,t3,hull); + + if ( hit1 || hit2 || hit3 ) + { + // now we build the little triangle mesh piece... + uint32_t indices[8*3]; + + float vertices[6*3]; + addVertex(p1,vertices,0); + addVertex(p2,vertices,1); + addVertex(p3,vertices,2); + addVertex(t1,vertices,3); + addVertex(t2,vertices,4); + addVertex(t3,vertices,5); + + addTriangle(indices,2,1,0,0); + addTriangle(indices,3,4,5,1); + + addTriangle(indices,0,3,4,2); + addTriangle(indices,0,4,1,3); + + addTriangle(indices,2,5,3,4); + addTriangle(indices,2,3,0,5); + + addTriangle(indices,1,4,5,6); + addTriangle(indices,1,5,2,7); + + ret = fm_computeMeshVolume(vertices,8,indices); + +#if 0 + static FILE *fph = fopen("project.obj", "wb" ); + static int baseVertex = 1; + for (int i=0; i<6; i++) + { + fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", vertices[i*3+0], vertices[i*3+1], vertices[i*3+2] ); + } + for (int i=0; i<8; i++) + { + fprintf(fph,"f %d %d %d\r\n", indices[i*3+0]+baseVertex, indices[i*3+1]+baseVertex, indices[i*3+2]+baseVertex ); + } + fflush(fph); + baseVertex+=6; +#endif + } + + return ret; +} +float computeConcavityVolume(uint32_t /*vcount*/, + const float *vertices, + uint32_t tcount, + const uint32_t *indices, + const HACD::HullResult &result) +{ + float ret = 0; + + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = indices[i*3+0]; + uint32_t i2 = indices[i*3+1]; + uint32_t i3 = indices[i*3+2]; + + const float *p1 = &vertices[i1*3]; + const float *p2 = &vertices[i2*3]; + const float *p3 = &vertices[i3*3]; + + ret+=computeProjectedVolume(p1,p2,p3,result); + + } + + return ret; +} + +// + + +typedef hacd::vector< ConvexResult > ConvexResultVector; + +class ConvexBuilder : public ConvexDecompInterface, public ConvexDecomposition, public UANS::UserAllocated +{ +public: + ConvexBuilder(void) + { + }; + + virtual ~ConvexBuilder(void) + { + for (uint32_t i=0; i<mResults.size(); i++) + { + ConvexResult &r = mResults[i]; + HACD_FREE(r.mHullIndices); + HACD_FREE(r.mHullVertices); + } + } + + virtual void ConvexDecompResult(const ConvexResult &result) + { + ConvexResult r; + r.mHullTcount = result.mHullTcount; + r.mHullVcount = result.mHullVcount; + r.mHullIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*r.mHullTcount*3); + memcpy(r.mHullIndices,result.mHullIndices,sizeof(uint32_t)*r.mHullTcount*3); + r.mHullVertices = (float *)HACD_ALLOC(sizeof(float)*r.mHullVcount*3); + memcpy(r.mHullVertices,result.mHullVertices,sizeof(float)*r.mHullVcount*3); + mResults.push_back(r); + } + +void doConvexDecomposition(uint32_t vcount, + const float *vertices, + uint32_t tcount, + const uint32_t *indices, + Cdesc &cdesc, + uint32_t depth) +{ + + // first see if the input mesh is co-planar. + // If it is, then we return because we can't do anything with a co-planer mesh + bool isCoplanar = fm_isMeshCoplanar(tcount,indices,vertices,true); + if ( isCoplanar ) return; + + + // Next build a convex hull for the input vertices for this mesh fragment + HACD::HullResult result; + HACD::HullLibrary hl; + HACD::HullDesc desc; + desc.mVcount = vcount; + desc.mVertices = vertices; + desc.mVertexStride = sizeof(float)*3; + HACD::HullError ret = hl.CreateConvexHull(desc,result); + if ( ret != HACD::QE_OK ) + { + return; // unable to build a hull for this remaining piece of mesh; so return. + } + + bool split = false; + if ( depth < cdesc.mMaxDepth ) // if have not reached the maximum depth + { + // compute the volume of the convex hull prior to the plist. + float hullVolume = fm_computeMeshVolume(result.mOutputVertices,result.mNumTriangles,result.mIndices); + if (depth == 0 ) + { + cdesc.mMasterVolume = hullVolume; + } + float percent = (hullVolume*100)/cdesc.mMasterVolume; + // if this convex hull is still considered significant enough in size to keep splitting... + if ( percent > cdesc.mMeshVolumePercent ) // if not too small of a feature... + { + // find the split plane by computing the OBB and slicing in half + float plane[4]; + split = fm_computeSplitPlane(result.mNumOutputVertices,result.mOutputVertices,result.mNumTriangles,result.mIndices,plane); + if ( split ) + { + { + float concaveVolume = computeConcavityVolume(vcount,vertices,tcount,indices,result); + float percentVolume = concaveVolume*100 / hullVolume; + + if ( percentVolume < cdesc.mConcavePercent ) + { + split = false; + } + } + + SimpleMesh mesh(vcount, tcount, vertices, indices); + SimpleMesh leftMesh; + SimpleMesh rightMesh; + splitMesh(plane,mesh,leftMesh,rightMesh); + + if ( split ) + { + + if ( leftMesh.mTcount ) + { + doConvexDecomposition(leftMesh.mVcount, leftMesh.mVertices, leftMesh.mTcount,leftMesh.mIndices,cdesc,depth+1); + } + if ( rightMesh.mTcount ) + { + doConvexDecomposition(rightMesh.mVcount, rightMesh.mVertices, rightMesh.mTcount,rightMesh.mIndices, cdesc, depth+1); + } + } + } + } + } + if ( !split ) + { + ConvexResult r; + r.mHullIndices = result.mIndices; + r.mHullVertices = result.mOutputVertices; + r.mHullTcount = result.mNumTriangles; + r.mHullVcount = result.mNumOutputVertices; + cdesc.mCallback->ConvexDecompResult(r); + hl.ReleaseResult(result); // do not release the result! + + if ( cdesc.mICallback ) + { + float progress = (float)cdesc.mOutputCount / (float)cdesc.mOutputPow2; + cdesc.mOutputCount++; + cdesc.mICallback->ReportProgress("SplittingMesh", progress ); + } + + + } +} + + virtual uint32_t performConvexDecomposition(const DecompDesc &desc) // returns the number of hulls produced. + { + Cdesc cdesc; + cdesc.mMaxDepth = desc.mDepth; + cdesc.mConcavePercent = desc.mCpercent; + cdesc.mMeshVolumePercent= desc.mMeshVolumePercent; + cdesc.mCallback = this; + cdesc.mICallback = desc.mCallback; + cdesc.mOutputCount = 0; + uint32_t p2[17] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 }; + if ( cdesc.mMaxDepth > 10 ) + cdesc.mMaxDepth = 10; + cdesc.mOutputPow2 = p2[ cdesc.mMaxDepth]; + doConvexDecomposition(desc.mVcount, desc.mVertices, desc.mTcount, desc.mIndices, cdesc, 0); + return mResults.size(); + } + + virtual void release(void) + { + delete this; + } + + virtual ConvexResult * getConvexResult(uint32_t index,bool takeMemoryOwnership) + { + ConvexResult *ret = NULL; + if ( index < mResults.size() ) + { + ret = &mResults[index]; + if ( takeMemoryOwnership ) + { + mTempResult = *ret; + ret->mHullIndices = NULL; + ret->mHullVertices = NULL; + ret = &mTempResult; + } + } + return ret; + } + + ConvexResult mTempResult; + ConvexResultVector mResults; +}; + +ConvexDecomposition * createConvexDecomposition(void) +{ + ConvexBuilder *m = HACD_NEW(ConvexBuilder); + return static_cast<ConvexDecomposition *>(m); +} + + +}; // end of namespace + |