/* NvStanHull.cpp : A convex hull generator written by Stan Melax */ /*! ** ** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com ** ** Portions of this source has been released with the PhysXViewer application, as well as ** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. ** ** If you find this code useful or you are feeling particularily generous I would ** ask that you please go to http://www.amillionpixels.us and make a donation ** to Troy DeMolay. ** ** DeMolay is a youth group for young men between the ages of 12 and 21. ** It teaches strong moral principles, as well as leadership skills and ** public speaking. The donations page uses the 'pay for pixels' paradigm ** where, in this case, a pixel is only a single penny. Donations can be ** made for as small as $4 or as high as a $100 block. Each person who donates ** will get a link to their own site as well as acknowledgement on the ** donations blog located here http://www.amillionpixels.blogspot.com/ ** ** If you wish to contact me you can use the following methods: ** ** Skype ID: jratcliff63367 ** Yahoo: jratcliff63367 ** AOL: jratcliff1961 ** email: jratcliffscarab@gmail.com ** ** ** 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. */ #include #include #include #include #include #include #include #include #include "NvStanHull.h" namespace CONVEX_DECOMPOSITION { //***************************************************** //*** DARRAY.H //***************************************************** template class ArrayRet; template class Array { public: Array(NxI32 s=0); Array(Array &array); Array(ArrayRet &array); ~Array(); void allocate(NxI32 s); void SetSize(NxI32 s); void Pack(); Type& Add(Type); void AddUnique(Type); NxI32 Contains(Type); void Insert(Type,NxI32); NxI32 IndexOf(Type); void Remove(Type); void DelIndex(NxI32 i); Type * element; NxI32 count; NxI32 array_size; const Type &operator[](NxI32 i) const { assert(i>=0 && i=0 && i &operator=(Array &array); Array &operator=(ArrayRet &array); // operator ArrayRet &() { return *(ArrayRet *)this;} // this worked but i suspect could be dangerous }; template class ArrayRet:public Array { }; template Array::Array(NxI32 s) { count=0; array_size = 0; element = NULL; if(s) { allocate(s); } } template Array::Array(Array &array) { count=0; array_size = 0; element = NULL; for(NxI32 i=0;i Array::Array(ArrayRet &array) { *this = array; } template Array &Array::operator=(ArrayRet &array) { count=array.count; array_size = array.array_size; element = array.element; array.element=NULL; array.count=0; array.array_size=0; return *this; } template Array &Array::operator=(Array &array) { count=0; for(NxI32 i=0;i Array::~Array() { if (element != NULL) { MEMALLOC_FREE(element); } count=0;array_size=0;element=NULL; } template void Array::allocate(NxI32 s) { assert(s>0); assert(s>=count); Type *old = element; array_size =s; element = (Type *) MEMALLOC_MALLOC( sizeof(Type)*array_size ); assert(element); for(NxI32 i=0;i void Array::SetSize(NxI32 s) { if(s==0) { if(element) { MEMALLOC_FREE(element); element = NULL; } array_size = s; } else { allocate(s); } count=s; } template void Array::Pack() { allocate(count); } template Type& Array::Add(Type t) { assert(count<=array_size); if(count==array_size) { allocate((array_size)?array_size *2:16); } element[count++] = t; return element[count-1]; } template NxI32 Array::Contains(Type t) { NxI32 i; NxI32 found=0; for(i=0;i void Array::AddUnique(Type t) { if(!Contains(t)) Add(t); } template void Array::DelIndex(NxI32 i) { assert(i void Array::Remove(Type t) { NxI32 i; for(i=0;i void Array::Insert(Type t,NxI32 k) { NxI32 i=count; Add(t); // to allocate space while(i>k) { element[i]=element[i-1]; i--; } assert(i==k); element[k]=t; } template NxI32 Array::IndexOf(Type t) { NxI32 i; for(i=0;i Member )))- ((char*)NULL)) NxI32 argmin(NxF32 a[],NxI32 n); NxF32 sqr(NxF32 a); NxF32 clampf(NxF32 a) ; NxF32 Round(NxF32 a,NxF32 precision); NxF32 Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha) ; template void Swap(T &a,T &b) { T tmp = a; a=b; b=tmp; } template T Max(const T &a,const T &b) { return (a>b)?a:b; } template T Min(const T &a,const T &b) { return (a=0&&i<2);return ((NxF32*)this)[i];} const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<2);return ((NxF32*)this)[i];} }; inline float2 operator-( const float2& a, const float2& b ){return float2(a.x-b.x,a.y-b.y);} inline float2 operator+( const float2& a, const float2& b ){return float2(a.x+b.x,a.y+b.y);} //--------- 3D --------- class float3 : public Memalloc // 3D { public: NxF32 x,y,z; float3(){x=0;y=0;z=0;}; float3(NxF32 _x,NxF32 _y,NxF32 _z){x=_x;y=_y;z=_z;}; //operator NxF32 *() { return &x;}; NxF32& operator[](NxI32 i) {assert(i>=0&&i<3);return ((NxF32*)this)[i];} const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<3);return ((NxF32*)this)[i];} }; float3& operator+=( float3 &a, const float3& b ); float3& operator-=( float3 &a ,const float3& b ); float3& operator*=( float3 &v ,const NxF32 s ); float3& operator/=( float3 &v, const NxF32 s ); NxF32 magnitude( const float3& v ); float3 normalize( const float3& v ); float3 safenormalize(const float3 &v); float3 vabs(const float3 &v); float3 operator+( const float3& a, const float3& b ); float3 operator-( const float3& a, const float3& b ); float3 operator-( const float3& v ); float3 operator*( const float3& v, const NxF32 s ); float3 operator*( const NxF32 s, const float3& v ); float3 operator/( const float3& v, const NxF32 s ); inline NxI32 operator==( const float3 &a, const float3 &b ) { return (a.x==b.x && a.y==b.y && a.z==b.z); } inline NxI32 operator!=( const float3 &a, const float3 &b ) { return (a.x!=b.x || a.y!=b.y || a.z!=b.z); } // due to ambiguity and inconsistent standards ther are no overloaded operators for mult such as va*vb. NxF32 dot( const float3& a, const float3& b ); float3 cmul( const float3 &a, const float3 &b); float3 cross( const float3& a, const float3& b ); float3 Interpolate(const float3 &v0,const float3 &v1,NxF32 alpha); float3 Round(const float3& a,NxF32 precision); float3 VectorMax(const float3 &a, const float3 &b); float3 VectorMin(const float3 &a, const float3 &b); class float3x3 : public Memalloc { public: float3 x,y,z; // the 3 rows of the Matrix float3x3(){} float3x3(NxF32 xx,NxF32 xy,NxF32 xz,NxF32 yx,NxF32 yy,NxF32 yz,NxF32 zx,NxF32 zy,NxF32 zz):x(xx,xy,xz),y(yx,yy,yz),z(zx,zy,zz){} float3x3(float3 _x,float3 _y,float3 _z):x(_x),y(_y),z(_z){} float3& operator[](NxI32 i) {assert(i>=0&&i<3);return (&x)[i];} const float3& operator[](NxI32 i) const {assert(i>=0&&i<3);return (&x)[i];} NxF32& operator()(NxI32 r, NxI32 c) {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];} const NxF32& operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];} }; float3x3 Transpose( const float3x3& m ); float3 operator*( const float3& v , const float3x3& m ); float3 operator*( const float3x3& m , const float3& v ); float3x3 operator*( const float3x3& m , const NxF32& s ); float3x3 operator*( const float3x3& ma, const float3x3& mb ); float3x3 operator/( const float3x3& a, const NxF32& s ) ; float3x3 operator+( const float3x3& a, const float3x3& b ); float3x3 operator-( const float3x3& a, const float3x3& b ); float3x3 &operator+=( float3x3& a, const float3x3& b ); float3x3 &operator-=( float3x3& a, const float3x3& b ); float3x3 &operator*=( float3x3& a, const NxF32& s ); NxF32 Determinant(const float3x3& m ); float3x3 Inverse(const float3x3& a); // its just 3x3 so we simply do that cofactor method //-------- 4D Math -------- class float4 : public Memalloc { public: NxF32 x,y,z,w; float4(){x=0;y=0;z=0;w=0;}; float4(NxF32 _x,NxF32 _y,NxF32 _z,NxF32 _w){x=_x;y=_y;z=_z;w=_w;} float4(const float3 &v,NxF32 _w){x=v.x;y=v.y;z=v.z;w=_w;} //operator NxF32 *() { return &x;}; NxF32& operator[](NxI32 i) {assert(i>=0&&i<4);return ((NxF32*)this)[i];} const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<4);return ((NxF32*)this)[i];} const float3& xyz() const { return *((float3*)this);} float3& xyz() { return *((float3*)this);} }; struct D3DXMATRIX; class float4x4 : public Memalloc { public: float4 x,y,z,w; // the 4 rows float4x4(){} float4x4(const float4 &_x, const float4 &_y, const float4 &_z, const float4 &_w):x(_x),y(_y),z(_z),w(_w){} float4x4(NxF32 m00, NxF32 m01, NxF32 m02, NxF32 m03, NxF32 m10, NxF32 m11, NxF32 m12, NxF32 m13, NxF32 m20, NxF32 m21, NxF32 m22, NxF32 m23, NxF32 m30, NxF32 m31, NxF32 m32, NxF32 m33 ) :x(m00,m01,m02,m03),y(m10,m11,m12,m13),z(m20,m21,m22,m23),w(m30,m31,m32,m33){} NxF32& operator()(NxI32 r, NxI32 c) {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];} const NxF32& operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];} operator NxF32* () {return &x.x;} operator const NxF32* () const {return &x.x;} operator struct D3DXMATRIX* () { return (struct D3DXMATRIX*) this;} operator const struct D3DXMATRIX* () const { return (struct D3DXMATRIX*) this;} }; NxI32 operator==( const float4 &a, const float4 &b ); float4 Homogenize(const float3 &v3,const NxF32 &w=1.0f); // Turns a 3D float3 4D vector4 by appending w float4 cmul( const float4 &a, const float4 &b); float4 operator*( const float4 &v, NxF32 s); float4 operator*( NxF32 s, const float4 &v); float4 operator+( const float4 &a, const float4 &b); float4 operator-( const float4 &a, const float4 &b); float4x4 operator*( const float4x4& a, const float4x4& b ); float4 operator*( const float4& v, const float4x4& m ); float4x4 Inverse(const float4x4 &m); float4x4 MatrixRigidInverse(const float4x4 &m); float4x4 MatrixTranspose(const float4x4 &m); float4x4 MatrixPerspectiveFov(NxF32 fovy, NxF32 Aspect, NxF32 zn, NxF32 zf ); float4x4 MatrixTranslation(const float3 &t); float4x4 MatrixRotationZ(const NxF32 angle_radians); float4x4 MatrixLookAt(const float3& eye, const float3& at, const float3& up); NxI32 operator==( const float4x4 &a, const float4x4 &b ); //-------- Quaternion ------------ class Quaternion :public float4 { public: Quaternion() { x = y = z = 0.0f; w = 1.0f; } Quaternion( float3 v, NxF32 t ) { v = normalize(v); w = cosf(t/2.0f); v = v*sinf(t/2.0f); x = v.x; y = v.y; z = v.z; } Quaternion(NxF32 _x, NxF32 _y, NxF32 _z, NxF32 _w){x=_x;y=_y;z=_z;w=_w;} NxF32 angle() const { return acosf(w)*2.0f; } float3 axis() const { float3 a(x,y,z); if(fabsf(angle())<0.0000001f) return float3(1,0,0); return a*(1/sinf(angle()/2.0f)); } float3 xdir() const { return float3( 1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-w*y) ); } float3 ydir() const { return float3( 2*(x*y-w*z),1-2*(x*x+z*z), 2*(y*z+w*x) ); } float3 zdir() const { return float3( 2*(x*z+w*y), 2*(y*z-w*x),1-2*(x*x+y*y) ); } float3x3 getmatrix() const { return float3x3( xdir(), ydir(), zdir() ); } operator float3x3() { return getmatrix(); } void Normalize(); }; Quaternion& operator*=(Quaternion& a, NxF32 s ); Quaternion operator*( const Quaternion& a, NxF32 s ); Quaternion operator*( const Quaternion& a, const Quaternion& b); Quaternion operator+( const Quaternion& a, const Quaternion& b ); Quaternion normalize( Quaternion a ); NxF32 dot( const Quaternion &a, const Quaternion &b ); float3 operator*( const Quaternion& q, const float3& v ); float3 operator*( const float3& v, const Quaternion& q ); Quaternion slerp( Quaternion a, const Quaternion& b, NxF32 interp ); Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha); Quaternion RotationArc(float3 v0, float3 v1 ); // returns quat q where q*v0=v1 Quaternion Inverse(const Quaternion &q); float4x4 MatrixFromQuatVec(const Quaternion &q, const float3 &v); //------ Euler Angle ----- Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll ); NxF32 Yaw( const Quaternion& q ); NxF32 Pitch( const Quaternion& q ); NxF32 Roll( Quaternion q ); NxF32 Yaw( const float3& v ); NxF32 Pitch( const float3& v ); //------- Plane ---------- class Plane { public: float3 normal; NxF32 dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0 Plane(const float3 &n,NxF32 d):normal(n),dist(d){} Plane():normal(),dist(0){} void Transform(const float3 &position, const Quaternion &orientation); }; inline Plane PlaneFlip(const Plane &plane){return Plane(-plane.normal,-plane.dist);} inline NxI32 operator==( const Plane &a, const Plane &b ) { return (a.normal==b.normal && a.dist==b.dist); } inline NxI32 coplanar( const Plane &a, const Plane &b ) { return (a==b || a==PlaneFlip(b)); } //--------- Utility Functions ------ float3 PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1); float3 PlaneProject(const Plane &plane, const float3 &point); float3 LineProject(const float3 &p0, const float3 &p1, const float3 &a); // projects a onto infinite line p0p1 NxF32 LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a); float3 ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2); NxI32 PolyHit(const float3 *vert,const NxI32 n,const float3 &v0, const float3 &v1, float3 *impact=NULL, float3 *normal=NULL); NxI32 BoxInside(const float3 &p,const float3 &bmin, const float3 &bmax) ; NxI32 BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax, float3 *impact); NxF32 DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint=NULL, float3 *vpoint=NULL); float3 TriNormal(const float3 &v0, const float3 &v1, const float3 &v2); float3 NormalOf(const float3 *vert, const NxI32 n); Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir0, const float3 &dir1); //***************************************************** // ** VECMATH.CPP //***************************************************** NxF32 sqr(NxF32 a) {return a*a;} NxF32 clampf(NxF32 a) {return Min(1.0f,Max(0.0f,a));} NxF32 Round(NxF32 a,NxF32 precision) { return floorf(0.5f+a/precision)*precision; } NxF32 Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha) { return f0*(1-alpha) + f1*alpha; } NxI32 argmin(NxF32 a[],NxI32 n) { NxI32 r=0; for(NxI32 i=1;i=1.0) { return a; } NxF32 theta = acosf(d); if(theta==0.0f) { return(a);} return a*(sinf(theta-interp*theta)/sinf(theta)) + b*(sinf(interp*theta)/sinf(theta)); } Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha) { return slerp(q0,q1,alpha); } Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll ) { roll *= DEG2RAD; yaw *= DEG2RAD; pitch *= DEG2RAD; return Quaternion(float3(0.0f,0.0f,1.0f),yaw)*Quaternion(float3(1.0f,0.0f,0.0f),pitch)*Quaternion(float3(0.0f,1.0f,0.0f),roll); } NxF32 Yaw( const Quaternion& q ) { static float3 v; v=q.ydir(); return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG; } NxF32 Pitch( const Quaternion& q ) { static float3 v; v=q.ydir(); return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG; } NxF32 Roll( Quaternion q ) { q = Quaternion(float3(0.0f,0.0f,1.0f),-Yaw(q)*DEG2RAD) *q; q = Quaternion(float3(1.0f,0.0f,0.0f),-Pitch(q)*DEG2RAD) *q; return atan2f(-q.xdir().z,q.xdir().x)*RAD2DEG; } NxF32 Yaw( const float3& v ) { return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG; } NxF32 Pitch( const float3& v ) { return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG; } //------------- Plane -------------- void Plane::Transform(const float3 &position, const Quaternion &orientation) { // Transforms the plane to the space defined by the // given position/orientation. static float3 newnormal; static float3 origin; newnormal = Inverse(orientation)*normal; origin = Inverse(orientation)*(-normal*dist - position); normal = newnormal; dist = -dot(newnormal, origin); } //--------- utility functions ------------- // RotationArc() // Given two vectors v0 and v1 this function // returns quaternion q where q*v0==v1. // Routine taken from game programming gems. Quaternion RotationArc(float3 v0,float3 v1){ static Quaternion q; v0 = normalize(v0); // Comment these two lines out if you know its not needed. v1 = normalize(v1); // If vector is already unit length then why do it again? float3 c = cross(v0,v1); NxF32 d = dot(v0,v1); if(d<=-1.0f) { return Quaternion(1,0,0,0);} // 180 about x axis NxF32 s = sqrtf((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; } float4x4 MatrixFromQuatVec(const Quaternion &q, const float3 &v) { // builds a 4x4 transformation matrix based on orientation q and translation v NxF32 qx2 = q.x*q.x; NxF32 qy2 = q.y*q.y; NxF32 qz2 = q.z*q.z; NxF32 qxqy = q.x*q.y; NxF32 qxqz = q.x*q.z; NxF32 qxqw = q.x*q.w; NxF32 qyqz = q.y*q.z; NxF32 qyqw = q.y*q.w; NxF32 qzqw = q.z*q.w; return float4x4( 1-2*(qy2+qz2), 2*(qxqy+qzqw), 2*(qxqz-qyqw), 0 , 2*(qxqy-qzqw), 1-2*(qx2+qz2), 2*(qyqz+qxqw), 0 , 2*(qxqz+qyqw), 2*(qyqz-qxqw), 1-2*(qx2+qy2), 0 , v.x , v.y , v.z , 1.0f ); } float3 PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1) { // returns the point where the line p0-p1 intersects the plane n&d static float3 dif; dif = p1-p0; NxF32 dn= dot(plane.normal,dif); NxF32 t = -(plane.dist+dot(plane.normal,p0) )/dn; return p0 + (dif*t); } float3 PlaneProject(const Plane &plane, const float3 &point) { return point - plane.normal * (dot(point,plane.normal)+plane.dist); } float3 LineProject(const float3 &p0, const float3 &p1, const float3 &a) { float3 w; w = p1-p0; NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z)); return p0+ w*t; } NxF32 LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a) { float3 w; w = p1-p0; NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z)); return t; } float3 TriNormal(const float3 &v0, const float3 &v1, const float3 &v2) { // return the normal of the triangle // inscribed by v0, v1, and v2 float3 cp=cross(v1-v0,v2-v1); NxF32 m=magnitude(cp); if(m==0) return float3(1,0,0); return cp*(1.0f/m); } NxI32 BoxInside(const float3 &p, const float3 &bmin, const float3 &bmax) { return (p.x >= bmin.x && p.x <=bmax.x && p.y >= bmin.y && p.y <=bmax.y && p.z >= bmin.z && p.z <=bmax.z ); } NxI32 BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax,float3 *impact) { if(BoxInside(v0,bmin,bmax)) { *impact=v0; return 1; } if(v0.x<=bmin.x && v1.x>=bmin.x) { NxF32 a = (bmin.x-v0.x)/(v1.x-v0.x); //v.x = bmin.x; NxF32 vy = (1-a) *v0.y + a*v1.y; NxF32 vz = (1-a) *v0.z + a*v1.z; if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z) { impact->x = bmin.x; impact->y = vy; impact->z = vz; return 1; } } else if(v0.x >= bmax.x && v1.x <= bmax.x) { NxF32 a = (bmax.x-v0.x)/(v1.x-v0.x); //v.x = bmax.x; NxF32 vy = (1-a) *v0.y + a*v1.y; NxF32 vz = (1-a) *v0.z + a*v1.z; if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z) { impact->x = bmax.x; impact->y = vy; impact->z = vz; return 1; } } if(v0.y<=bmin.y && v1.y>=bmin.y) { NxF32 a = (bmin.y-v0.y)/(v1.y-v0.y); NxF32 vx = (1-a) *v0.x + a*v1.x; //v.y = bmin.y; NxF32 vz = (1-a) *v0.z + a*v1.z; if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z) { impact->x = vx; impact->y = bmin.y; impact->z = vz; return 1; } } else if(v0.y >= bmax.y && v1.y <= bmax.y) { NxF32 a = (bmax.y-v0.y)/(v1.y-v0.y); NxF32 vx = (1-a) *v0.x + a*v1.x; // vy = bmax.y; NxF32 vz = (1-a) *v0.z + a*v1.z; if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z) { impact->x = vx; impact->y = bmax.y; impact->z = vz; return 1; } } if(v0.z<=bmin.z && v1.z>=bmin.z) { NxF32 a = (bmin.z-v0.z)/(v1.z-v0.z); NxF32 vx = (1-a) *v0.x + a*v1.x; NxF32 vy = (1-a) *v0.y + a*v1.y; // v.z = bmin.z; if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x) { impact->x = vx; impact->y = vy; impact->z = bmin.z; return 1; } } else if(v0.z >= bmax.z && v1.z <= bmax.z) { NxF32 a = (bmax.z-v0.z)/(v1.z-v0.z); NxF32 vx = (1-a) *v0.x + a*v1.x; NxF32 vy = (1-a) *v0.y + a*v1.y; // v.z = bmax.z; if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x) { impact->x = vx; impact->y = vy; impact->z = bmax.z; return 1; } } return 0; } NxF32 DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint, float3 *vpoint) { static float3 cp; cp = normalize(cross(udir,vdir)); NxF32 distu = -dot(cp,ustart); NxF32 distv = -dot(cp,vstart); NxF32 dist = (NxF32)fabs(distu-distv); if(upoint) { Plane plane; plane.normal = normalize(cross(vdir,cp)); plane.dist = -dot(plane.normal,vstart); *upoint = PlaneLineIntersection(plane,ustart,ustart+udir); } if(vpoint) { Plane plane; plane.normal = normalize(cross(udir,cp)); plane.dist = -dot(plane.normal,ustart); *vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir); } return dist; } Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir1, const float3 &dir2) { // routine taken from game programming gems. // Implement track ball functionality to spin stuf on the screen // cop center of projection // cor center of rotation // dir1 old mouse direction // dir2 new mouse direction // pretend there is a sphere around cor. Then find the points // where dir1 and dir2 intersect that sphere. Find the // rotation that takes the first point to the second. NxF32 m; // compute plane float3 nrml = cor - cop; NxF32 fudgefactor = 1.0f/(magnitude(nrml) * 0.25f); // since trackball proportional to distance from cop nrml = normalize(nrml); NxF32 dist = -dot(nrml,cor); float3 u= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir1); u=u-cor; u=u*fudgefactor; m= magnitude(u); if(m>1) { u/=m; } else { u=u - (nrml * sqrtf(1-m*m)); } float3 v= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir2); v=v-cor; v=v*fudgefactor; m= magnitude(v); if(m>1) { v/=m; } else { v=v - (nrml * sqrtf(1-m*m)); } return RotationArc(u,v); } NxI32 countpolyhit=0; NxI32 PolyHit(const float3 *vert, const NxI32 n, const float3 &v0, const float3 &v1, float3 *impact, float3 *normal) { countpolyhit++; NxI32 i; float3 nrml(0,0,0); for(i=0;i0) { return 0; } static float3 the_point; // By using the cached plane distances d0 and d1 // we can optimize the following: // the_point = planelineintersection(nrml,dist,v0,v1); NxF32 a = d0/(d0-d1); the_point = v0*(1-a) + v1*a; NxI32 inside=1; for(NxI32 j=0;inside && j= 0.0); } if(inside) { if(normal){*normal=nrml;} if(impact){*impact=the_point;} } return inside; } //**************************************************** // HULL.H source code goes here //**************************************************** class PHullResult { public: PHullResult(void) { mVcount = 0; mIndexCount = 0; mFaceCount = 0; mVertices = 0; mIndices = 0; } NxU32 mVcount; NxU32 mIndexCount; NxU32 mFaceCount; NxF32 *mVertices; NxU32 *mIndices; }; bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 maxverts,NxF32 inflate); void ReleaseHull(PHullResult &result); //***************************************************** // HULL.cpp source code goes here //***************************************************** #define REAL3 float3 #define REAL NxF32 #define COPLANAR (0) #define UNDER (1) #define OVER (2) #define SPLIT (OVER|UNDER) #define PAPERWIDTH (0.001f) #define VOLUME_EPSILON (1e-20f) NxF32 planetestepsilon = PAPERWIDTH; class ConvexH : public Memalloc { public: class HalfEdge { public: short ea; // the other half of the edge (index into edges list) NxU8 v; // the vertex at the start of this edge (index into vertices list) NxU8 p; // the facet on which this edge lies (index into facets list) HalfEdge(){} HalfEdge(short _ea,NxU8 _v, NxU8 _p):ea(_ea),v(_v),p(_p){} }; Array vertices; Array edges; Array facets; ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size); }; typedef ConvexH::HalfEdge HalfEdge; ConvexH::ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size) :vertices(vertices_size) ,edges(edges_size) ,facets(facets_size) { vertices.count=vertices_size; edges.count = edges_size; facets.count = facets_size; } ConvexH *ConvexHDup(ConvexH *src) { ConvexH *dst = MEMALLOC_NEW(ConvexH)(src->vertices.count,src->edges.count,src->facets.count); memcpy(dst->vertices.element,src->vertices.element,sizeof(float3)*src->vertices.count); memcpy(dst->edges.element,src->edges.element,sizeof(HalfEdge)*src->edges.count); memcpy(dst->facets.element,src->facets.element,sizeof(Plane)*src->facets.count); return dst; } NxI32 PlaneTest(const Plane &p, const REAL3 &v) { REAL a = dot(v,p.normal)+p.dist; NxI32 flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR); return flag; } NxI32 SplitTest(ConvexH &convex,const Plane &plane) { NxI32 flag=0; for(NxI32 i=0;i= convex.edges.count || convex.edges[inext].p != convex.edges[i].p) { inext = estart; } assert(convex.edges[inext].p == convex.edges[i].p); NxI32 nb = convex.edges[i].ea; assert(nb!=255); if(nb==255 || nb==-1) return 0; assert(nb!=-1); assert(i== convex.edges[nb].ea); } for(i=0;i= convex.edges.count || convex.edges[i1].p != convex.edges[i].p) { i1 = estart; } NxI32 i2 = i1+1; if(i2>= convex.edges.count || convex.edges[i2].p != convex.edges[i].p) { i2 = estart; } if(i==i2) continue; // i sliced tangent to an edge and created 2 meaningless edges REAL3 localnormal = TriNormal(convex.vertices[convex.edges[i ].v], convex.vertices[convex.edges[i1].v], convex.vertices[convex.edges[i2].v]); //assert(dot(localnormal,convex.facets[convex.edges[i].p].normal)>0);//Commented out on Stan Melax' advice if(dot(localnormal,convex.facets[convex.edges[i].p].normal)<=0)return 0; } return 1; } ConvexH *ConvexHCrop(ConvexH &convex,const Plane &slice) { NxI32 i; NxI32 vertcountunder=0; NxI32 vertcountover =0; static Array vertscoplanar; // existing vertex members of convex that are coplanar vertscoplanar.count=0; static Array edgesplit; // existing edges that members of convex that cross the splitplane edgesplit.count=0; assert(convex.edges.count<480); EdgeFlag edgeflag[512]; VertFlag vertflag[256]; PlaneFlag planeflag[128]; HalfEdge tmpunderedges[512]; Plane tmpunderplanes[128]; Coplanar coplanaredges[512]; NxI32 coplanaredges_num=0; Array createdverts; // do the side-of-plane tests for(i=0;i= convex.edges.count || convex.edges[e1].p!=currentplane) { enextface = e1; e1=estart; } HalfEdge &edge0 = convex.edges[e0]; HalfEdge &edge1 = convex.edges[e1]; HalfEdge &edgea = convex.edges[edge0.ea]; planeside |= vertflag[edge0.v].planetest; //if((vertflag[edge0.v].planetest & vertflag[edge1.v].planetest) == COPLANAR) { // assert(ecop==-1); // ecop=e; //} if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == OVER){ // both endpoints over plane edgeflag[e0].undermap = -1; } else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == UNDER) { // at least one endpoint under, the other coplanar or under edgeflag[e0].undermap = (short)under_edge_count; tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; tmpunderedges[under_edge_count].p = (NxU8)underplanescount; if(edge0.ea < e0) { // connect the neighbors assert(edgeflag[edge0.ea].undermap !=-1); tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; } under_edge_count++; } else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == COPLANAR) { // both endpoints coplanar // must check a 3rd point to see if UNDER NxI32 e2 = e1+1; if(e2>=convex.edges.count || convex.edges[e2].p!=currentplane) { e2 = estart; } assert(convex.edges[e2].p==currentplane); HalfEdge &edge2 = convex.edges[e2]; if(vertflag[edge2.v].planetest==UNDER) { edgeflag[e0].undermap = (short)under_edge_count; tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; tmpunderedges[under_edge_count].p = (NxU8)underplanescount; tmpunderedges[under_edge_count].ea = -1; // make sure this edge is added to the "coplanar" list coplanaredge = under_edge_count; vout = vertflag[edge0.v].undermap; vin = vertflag[edge1.v].undermap; under_edge_count++; } else { edgeflag[e0].undermap = -1; } } else if(vertflag[edge0.v].planetest == UNDER && vertflag[edge1.v].planetest == OVER) { // first is under 2nd is over edgeflag[e0].undermap = (short) under_edge_count; tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; tmpunderedges[under_edge_count].p = (NxU8)underplanescount; if(edge0.ea < e0) { assert(edgeflag[edge0.ea].undermap !=-1); // connect the neighbors tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; vout = tmpunderedges[edgeflag[edge0.ea].undermap].v; } else { Plane &p0 = convex.facets[edge0.p]; Plane &pa = convex.facets[edgea.p]; createdverts.Add(ThreePlaneIntersection(p0,pa,slice)); //createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]))); //createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])); vout = vertcountunder++; } under_edge_count++; /// hmmm something to think about: i might be able to output this edge regarless of // wheter or not we know v-in yet. ok i;ll try this now: tmpunderedges[under_edge_count].v = (NxU8)vout; tmpunderedges[under_edge_count].p = (NxU8)underplanescount; tmpunderedges[under_edge_count].ea = -1; coplanaredge = under_edge_count; under_edge_count++; if(vin!=-1) { // we previously processed an edge where we came under // now we know about vout as well // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! } } else if(vertflag[edge0.v].planetest == COPLANAR && vertflag[edge1.v].planetest == OVER) { // first is coplanar 2nd is over edgeflag[e0].undermap = -1; vout = vertflag[edge0.v].undermap; // I hate this but i have to make sure part of this face is UNDER before ouputting this vert NxI32 k=estart; assert(edge0.p == currentplane); while(!(planeside&UNDER) && kedge0.ea) { assert(edgeflag[edge0.ea].undermap !=-1); // connect the neighbors tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; } assert(edgeflag[e0].undermap == under_edge_count); under_edge_count++; } else if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == COPLANAR) { // first is over next is coplanar edgeflag[e0].undermap = -1; vin = vertflag[edge1.v].undermap; if (vin==-1) return NULL; if(vout!=-1) { // we previously processed an edge where we came under // now we know both endpoints // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! } } else { assert(0); } e0=e1; e1++; // do the modulo at the beginning of the loop } while(e0!=estart) ; e0 = enextface; if(planeside&UNDER) { planeflag[currentplane].undermap = (NxU8)underplanescount; tmpunderplanes[underplanescount] = convex.facets[currentplane]; underplanescount++; } else { planeflag[currentplane].undermap = 0; } if(vout>=0 && (planeside&UNDER)) { assert(vin>=0); assert(coplanaredge>=0); assert(coplanaredge!=511); coplanaredges[coplanaredges_num].ea = (short)coplanaredge; coplanaredges[coplanaredges_num].v0 = (NxU8)vin; coplanaredges[coplanaredges_num].v1 = (NxU8)vout; coplanaredges_num++; } } // add the new plane to the mix: if(coplanaredges_num>0) { tmpunderplanes[underplanescount++]=slice; } for(i=0;i=coplanaredges_num) { // assert(jvertices.count;j++) { dmax = Max(dmax,dot(convex->vertices[j],planes[i].normal)+planes[i].dist); dmin = Min(dmin,dot(convex->vertices[j],planes[i].normal)+planes[i].dist); } NxF32 dr = dmax-dmin; if(drfacets.count;j++) { if(planes[i]==convex->facets[j]) { d=0;continue; } if(dot(planes[i].normal,convex->facets[j].normal)>maxdot_minang) { for(NxI32 k=0;kedges.count;k++) { if(convex->edges[k].p!=j) continue; if(dot(convex->vertices[convex->edges[k].v],planes[i].normal)+planes[i].dist<0) { d=0; // so this plane wont get selected. break; } } } } if(d>md) { p=i; md=d; } } return (md>epsilon)?p:-1; } template inline NxI32 maxdir(const T *p,NxI32 count,const T &dir) { assert(count); NxI32 m=0; for(NxI32 i=1;idot(p[m],dir)) m=i; } return m; } template NxI32 maxdirfiltered(const T *p,NxI32 count,const T &dir,Array &allow) { assert(count); NxI32 m=-1; for(NxI32 i=0;idot(p[m],dir)) m=i; } assert(m!=-1); return m; } float3 orth(const float3 &v) { float3 a=cross(v,float3(0,0,1)); float3 b=cross(v,float3(0,1,0)); return normalize((magnitude(a)>magnitude(b))?a:b); } template NxI32 maxdirsterid(const T *p,NxI32 count,const T &dir,Array &allow) { NxI32 m=-1; while(m==-1) { m = maxdirfiltered(p,count,dir,allow); if(allow[m]==3) return m; T u = orth(dir); T v = cross(u,dir); NxI32 ma=-1; for(NxF32 x = 0.0f ; x<= 360.0f ; x+= 45.0f) { NxF32 s = sinf(DEG2RAD*(x)); NxF32 c = cosf(DEG2RAD*(x)); NxI32 mb = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow); if(ma==m && mb==m) { allow[m]=3; return m; } if(ma!=-1 && ma!=mb) // Yuck - this is really ugly { NxI32 mc = ma; for(NxF32 xx = x-40.0f ; xx <= x ; xx+= 5.0f) { NxF32 s = sinf(DEG2RAD*(xx)); NxF32 c = cosf(DEG2RAD*(xx)); NxI32 md = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow); if(mc==m && md==m) { allow[m]=3; return m; } mc=md; } } ma=mb; } allow[m]=0; m=-1; } assert(0); return m; } NxI32 operator ==(const int3 &a,const int3 &b) { for(NxI32 i=0;i<3;i++) { if(a[i]!=b[i]) return 0; } return 1; } int3 roll3(int3 a) { NxI32 tmp=a[0]; a[0]=a[1]; a[1]=a[2]; a[2]=tmp; return a; } NxI32 isa(const int3 &a,const int3 &b) { return ( a==b || roll3(a)==b || a==roll3(b) ); } NxI32 b2b(const int3 &a,const int3 &b) { return isa(a,int3(b[2],b[1],b[0])); } NxI32 above(float3* vertices,const int3& t, const float3 &p, NxF32 epsilon) { float3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]); return (dot(n,p-vertices[t[0]]) > epsilon); // EPSILON??? } NxI32 hasedge(const int3 &t, NxI32 a,NxI32 b) { for(NxI32 i=0;i<3;i++) { NxI32 i1= (i+1)%3; if(t[i]==a && t[i1]==b) return 1; } return 0; } NxI32 hasvert(const int3 &t, NxI32 v) { return (t[0]==v || t[1]==v || t[2]==v) ; } NxI32 shareedge(const int3 &a,const int3 &b) { NxI32 i; for(i=0;i<3;i++) { NxI32 i1= (i+1)%3; if(hasedge(a,b[i1],b[i])) return 1; } return 0; } class Tri; static Array tris; // djs: For heaven's sake!!!! class Tri : public int3 { public: int3 n; NxI32 id; NxI32 vmax; NxF32 rise; Tri(NxI32 a,NxI32 b,NxI32 c):int3(a,b,c),n(-1,-1,-1) { id = tris.count; tris.Add(this); vmax=-1; rise = 0.0f; } ~Tri() { assert(tris[id]==this); tris[id]=NULL; } NxI32 &neib(NxI32 a,NxI32 b); }; NxI32 &Tri::neib(NxI32 a,NxI32 b) { static NxI32 er=-1; NxI32 i; for(i=0;i<3;i++) { NxI32 i1=(i+1)%3; NxI32 i2=(i+2)%3; if((*this)[i]==a && (*this)[i1]==b) return n[i2]; if((*this)[i]==b && (*this)[i1]==a) return n[i2]; } assert(0); return er; } void b2bfix(Tri* s,Tri*t) { NxI32 i; for(i=0;i<3;i++) { NxI32 i1=(i+1)%3; NxI32 i2=(i+2)%3; NxI32 a = (*s)[i1]; NxI32 b = (*s)[i2]; assert(tris[s->neib(a,b)]->neib(b,a) == s->id); assert(tris[t->neib(a,b)]->neib(b,a) == t->id); tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a); tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b); } } void removeb2b(Tri* s,Tri*t) { b2bfix(s,t); delete s; delete t; } void extrude(Tri *t0,NxI32 v) { int3 t= *t0; NxI32 n = tris.count; Tri* ta = MEMALLOC_NEW(Tri)(v,t[1],t[2]); ta->n = int3(t0->n[0],n+1,n+2); tris[t0->n[0]]->neib(t[1],t[2]) = n+0; Tri* tb = MEMALLOC_NEW(Tri)(v,t[2],t[0]); tb->n = int3(t0->n[1],n+2,n+0); tris[t0->n[1]]->neib(t[2],t[0]) = n+1; Tri* tc = MEMALLOC_NEW(Tri)(v,t[0],t[1]); tc->n = int3(t0->n[2],n+0,n+1); tris[t0->n[2]]->neib(t[0],t[1]) = n+2; if(hasvert(*tris[ta->n[0]],v)) removeb2b(ta,tris[ta->n[0]]); if(hasvert(*tris[tb->n[0]],v)) removeb2b(tb,tris[tb->n[0]]); if(hasvert(*tris[tc->n[0]],v)) removeb2b(tc,tris[tc->n[0]]); delete t0; } Tri *extrudable(NxF32 epsilon) { NxI32 i; Tri *t=NULL; for(i=0;iriserise)) { t = tris[i]; } } return (t->rise >epsilon)?t:NULL ; } class int4 { public: NxI32 x,y,z,w; int4(){}; int4(NxI32 _x,NxI32 _y, NxI32 _z,NxI32 _w){x=_x;y=_y;z=_z;w=_w;} const NxI32& operator[](NxI32 i) const {return (&x)[i];} NxI32& operator[](NxI32 i) {return (&x)[i];} }; bool hasVolume(float3 *verts, NxI32 p0, NxI32 p1, NxI32 p2, NxI32 p3) { float3 result3 = cross(verts[p1]-verts[p0], verts[p2]-verts[p0]); if (magnitude(result3) < VOLUME_EPSILON && magnitude(result3) > -VOLUME_EPSILON) // Almost collinear or otherwise very close to each other return false; NxF32 result = dot(normalize(result3), verts[p3]-verts[p0]); return (result > VOLUME_EPSILON || result < -VOLUME_EPSILON); // Returns true iff volume is significantly non-zero } int4 FindSimplex(float3 *verts,NxI32 verts_count,Array &allow) { float3 basis[3]; basis[0] = float3( 0.01f, 0.02f, 1.0f ); NxI32 p0 = maxdirsterid(verts,verts_count, basis[0],allow); NxI32 p1 = maxdirsterid(verts,verts_count,-basis[0],allow); basis[0] = verts[p0]-verts[p1]; if(p0==p1 || basis[0]==float3(0,0,0)) return int4(-1,-1,-1,-1); basis[1] = cross(float3( 1, 0.02f, 0),basis[0]); basis[2] = cross(float3(-0.02f, 1, 0),basis[0]); basis[1] = normalize( (magnitude(basis[1])>magnitude(basis[2])) ? basis[1]:basis[2]); NxI32 p2 = maxdirsterid(verts,verts_count,basis[1],allow); if(p2 == p0 || p2 == p1) { p2 = maxdirsterid(verts,verts_count,-basis[1],allow); } if(p2 == p0 || p2 == p1) return int4(-1,-1,-1,-1); basis[1] = verts[p2] - verts[p0]; basis[2] = normalize(cross(basis[1],basis[0])); NxI32 p3 = maxdirsterid(verts,verts_count,basis[2],allow); if(p3==p0||p3==p1||p3==p2||!hasVolume(verts, p0, p1, p2, p3)) p3 = maxdirsterid(verts,verts_count,-basis[2],allow); if(p3==p0||p3==p1||p3==p2) return int4(-1,-1,-1,-1); assert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3)); if(dot(verts[p3]-verts[p0],cross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {Swap(p2,p3);} return int4(p0,p1,p2,p3); } #pragma warning(push) #pragma warning(disable:4706) NxI32 calchullgen(float3 *verts,NxI32 verts_count, NxI32 vlimit) { if(verts_count <4) return 0; if(vlimit==0) vlimit=1000000000; NxI32 j; float3 bmin(*verts),bmax(*verts); Array isextreme(verts_count); Array allow(verts_count); for(j=0;jn=int3(2,3,1); Tri *t1 = MEMALLOC_NEW(Tri)(p[3],p[2],p[0]); t1->n=int3(3,2,0); Tri *t2 = MEMALLOC_NEW(Tri)(p[0],p[1],p[3]); t2->n=int3(0,1,3); Tri *t3 = MEMALLOC_NEW(Tri)(p[1],p[0],p[2]); t3->n=int3(1,0,2); isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1; for(j=0;jvmax<0); float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); t->vmax = maxdirsterid(verts,verts_count,n,allow); t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]); } Tri *te; vlimit-=4; while(vlimit >0 && (te=extrudable(epsilon))) { int3 ti=*te; NxI32 v=te->vmax; assert(!isextreme[v]); // wtf we've already done this vertex isextreme[v]=1; //if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already j=tris.count; while(j--) { if(!tris[j]) continue; int3 t=*tris[j]; if(above(verts,t,verts[v],0.01f*epsilon)) { extrude(tris[j],v); } } // now check for those degenerate cases where we have a flipped triangle or a really skinny triangle j=tris.count; while(j--) { if(!tris[j]) continue; if(!hasvert(*tris[j],v)) break; int3 nt=*tris[j]; if(above(verts,nt,center,0.01f*epsilon) || magnitude(cross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]))< epsilon*epsilon*0.1f ) { Tri *nb = tris[tris[j]->n[0]]; assert(nb);assert(!hasvert(*nb,v));assert(nb->idvmax>=0) break; float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); t->vmax = maxdirsterid(verts,verts_count,n,allow); if(isextreme[t->vmax]) { t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate. } else { t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]); } } vlimit --; } return 1; } #pragma warning(pop) NxI32 calchull(float3 *verts,NxI32 verts_count, NxI32 *&tris_out, NxI32 &tris_count,NxI32 vlimit) { NxI32 rc=calchullgen(verts,verts_count, vlimit) ; if(!rc) return 0; Array ts; for(NxI32 i=0;i &planes,NxF32 bevangle) { NxI32 i,j; Array bplanes; planes.count=0; NxI32 rc = calchullgen(verts,verts_count,vlimit); if(!rc) return 0; extern NxF32 minadjangle; // default is 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. NxF32 maxdot_minang = cosf(DEG2RAD*minadjangle); for(i=0;in[j]id) continue; Tri *s = tris[t->n[j]]; REAL3 snormal = TriNormal(verts[(*s)[0]],verts[(*s)[1]],verts[(*s)[2]]); if(dot(snormal,p.normal)>=cos(bevangle*DEG2RAD)) continue; REAL3 e = verts[(*t)[(j+2)%3]] - verts[(*t)[(j+1)%3]]; REAL3 n = (e!=REAL3(0,0,0))? cross(snormal,e)+cross(e,p.normal) : snormal+p.normal; assert(n!=REAL3(0,0,0)); if(n==REAL3(0,0,0)) return 0; n=normalize(n); bplanes.Add(Plane(n,-dot(n,verts[maxdir(verts,verts_count,n)]))); } } for(i=0;imaxdot_minang) { // somebody has to die, keep the biggest triangle if( area2(verts[(*ti)[0]],verts[(*ti)[1]],verts[(*ti)[2]]) < area2(verts[(*tj)[0]],verts[(*tj)[1]],verts[(*tj)[2]])) { delete tris[i]; } else { delete tris[j]; } } } for(i=0;imaxdot_minang) break; } if(j==planes.count) { planes.Add(bplanes[i]); } } for(i=0;ivertices[0] = REAL3(0,0,0); convex->vertices[1] = REAL3(0,0,1); convex->vertices[2] = REAL3(0,1,0); convex->vertices[3] = REAL3(0,1,1); convex->vertices[4] = REAL3(1,0,0); convex->vertices[5] = REAL3(1,0,1); convex->vertices[6] = REAL3(1,1,0); convex->vertices[7] = REAL3(1,1,1); convex->facets[0] = Plane(REAL3(-1,0,0),0); convex->facets[1] = Plane(REAL3(1,0,0),-1); convex->facets[2] = Plane(REAL3(0,-1,0),0); convex->facets[3] = Plane(REAL3(0,1,0),-1); convex->facets[4] = Plane(REAL3(0,0,-1),0); convex->facets[5] = Plane(REAL3(0,0,1),-1); convex->edges[0 ] = HalfEdge(11,0,0); convex->edges[1 ] = HalfEdge(23,1,0); convex->edges[2 ] = HalfEdge(15,3,0); convex->edges[3 ] = HalfEdge(16,2,0); convex->edges[4 ] = HalfEdge(13,6,1); convex->edges[5 ] = HalfEdge(21,7,1); convex->edges[6 ] = HalfEdge( 9,5,1); convex->edges[7 ] = HalfEdge(18,4,1); convex->edges[8 ] = HalfEdge(19,0,2); convex->edges[9 ] = HalfEdge( 6,4,2); convex->edges[10] = HalfEdge(20,5,2); convex->edges[11] = HalfEdge( 0,1,2); convex->edges[12] = HalfEdge(22,3,3); convex->edges[13] = HalfEdge( 4,7,3); convex->edges[14] = HalfEdge(17,6,3); convex->edges[15] = HalfEdge( 2,2,3); convex->edges[16] = HalfEdge( 3,0,4); convex->edges[17] = HalfEdge(14,2,4); convex->edges[18] = HalfEdge( 7,6,4); convex->edges[19] = HalfEdge( 8,4,4); convex->edges[20] = HalfEdge(10,1,5); convex->edges[21] = HalfEdge( 5,5,5); convex->edges[22] = HalfEdge(12,7,5); convex->edges[23] = HalfEdge( 1,3,5); return convex; } ConvexH *ConvexHMakeCube(const REAL3 &bmin, const REAL3 &bmax) { ConvexH *convex = test_cube(); convex->vertices[0] = REAL3(bmin.x,bmin.y,bmin.z); convex->vertices[1] = REAL3(bmin.x,bmin.y,bmax.z); convex->vertices[2] = REAL3(bmin.x,bmax.y,bmin.z); convex->vertices[3] = REAL3(bmin.x,bmax.y,bmax.z); convex->vertices[4] = REAL3(bmax.x,bmin.y,bmin.z); convex->vertices[5] = REAL3(bmax.x,bmin.y,bmax.z); convex->vertices[6] = REAL3(bmax.x,bmax.y,bmin.z); convex->vertices[7] = REAL3(bmax.x,bmax.y,bmax.z); convex->facets[0] = Plane(REAL3(-1,0,0), bmin.x); convex->facets[1] = Plane(REAL3(1,0,0), -bmax.x); convex->facets[2] = Plane(REAL3(0,-1,0), bmin.y); convex->facets[3] = Plane(REAL3(0,1,0), -bmax.y); convex->facets[4] = Plane(REAL3(0,0,-1), bmin.z); convex->facets[5] = Plane(REAL3(0,0,1), -bmax.z); return convex; } static NxI32 overhull(Plane *planes,NxI32 planes_count,float3 *verts, NxI32 verts_count,NxI32 maxplanes, float3 *&verts_out, NxI32 &verts_count_out, NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate) { NxI32 i,j; if(verts_count <4) return NULL; maxplanes = Min(maxplanes,planes_count); float3 bmin(verts[0]),bmax(verts[0]); for(i=0;i maxdot_minang) { (*((j%2)?&bmax:&bmin)) += n * (diameter*0.5f); break; } } } ConvexH *c = ConvexHMakeCube(REAL3(bmin),REAL3(bmax)); NxI32 k; while(maxplanes-- && (k=candidateplane(planes,planes_count,c,epsilon))>=0) { ConvexH *tmp = c; c = ConvexHCrop(*tmp,planes[k]); if(c==NULL) {c=tmp; break;} // might want to debug this case better!!! if(!AssertIntact(*c)) {c=tmp; break;} // might want to debug this case better too!!! delete tmp; } assert(AssertIntact(*c)); //return c; faces_out = (NxI32*)MEMALLOC_MALLOC(sizeof(NxI32)*(1+c->facets.count+c->edges.count)); // new NxI32[1+c->facets.count+c->edges.count]; faces_count_out=0; i=0; faces_out[faces_count_out++]=-1; k=0; while(iedges.count) { j=1; while(j+iedges.count && c->edges[i].p==c->edges[i+j].p) { j++; } faces_out[faces_count_out++]=j; while(j--) { faces_out[faces_count_out++] = c->edges[i].v; i++; } k++; } faces_out[0]=k; // number of faces. assert(k==c->facets.count); assert(faces_count_out == 1+c->facets.count+c->edges.count); verts_out = c->vertices.element; // new float3[c->vertices.count]; verts_count_out = c->vertices.count; for(i=0;ivertices.count;i++) { verts_out[i] = float3(c->vertices[i]); } c->vertices.count=c->vertices.array_size=0; c->vertices.element=NULL; delete c; return 1; } static NxI32 overhullv(float3 *verts, NxI32 verts_count,NxI32 maxplanes, float3 *&verts_out, NxI32 &verts_count_out, NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate,NxF32 bevangle,NxI32 vlimit) { if(!verts_count) return 0; extern NxI32 calchullpbev(float3 *verts,NxI32 verts_count,NxI32 vlimit, Array &planes,NxF32 bevangle) ; Array planes; NxI32 rc=calchullpbev(verts,verts_count,vlimit,planes,bevangle) ; if(!rc) return 0; return overhull(planes.element,planes.count,verts,verts_count,maxplanes,verts_out,verts_count_out,faces_out,faces_count_out,inflate); } //***************************************************** //***************************************************** bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 vlimit,NxF32 inflate) { NxI32 index_count; NxI32 *faces; float3 *verts_out; NxI32 verts_count_out; if(inflate==0.0f) { NxI32 *tris_out; NxI32 tris_count; NxI32 ret = calchull( (float3 *) vertices, (NxI32) vcount, tris_out, tris_count, vlimit ); if(!ret) return false; result.mIndexCount = (NxU32) (tris_count*3); result.mFaceCount = (NxU32) tris_count; result.mVertices = (NxF32*) vertices; result.mVcount = (NxU32) vcount; result.mIndices = (NxU32 *) tris_out; return true; } NxI32 ret = overhullv((float3*)vertices,vcount,35,verts_out,verts_count_out,faces,index_count,inflate,120.0f,vlimit); if(!ret) { tris.SetSize(0); //have to set the size to 0 in order to protect from a "pure virtual function call" problem return false; } Array tris; NxI32 n=faces[0]; NxI32 k=1; for(NxI32 i=0;i bmax[j] ) bmax[j] = p[j]; } } } NxF32 dx = bmax[0] - bmin[0]; NxF32 dy = bmax[1] - bmin[1]; NxF32 dz = bmax[2] - bmin[2]; NxF32 center[3]; center[0] = dx*0.5f + bmin[0]; center[1] = dy*0.5f + bmin[1]; center[2] = dz*0.5f + bmin[2]; if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 ) { NxF32 len = FLT_MAX; if ( dx > EPSILON && dx < len ) len = dx; if ( dy > EPSILON && dy < len ) len = dy; if ( dz > EPSILON && dz < len ) len = dz; if ( len == FLT_MAX ) { dx = dy = dz = 0.01f; // one centimeter } else { if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge. if ( dy < EPSILON ) dy = len * 0.05f; if ( dz < EPSILON ) dz = len * 0.05f; } NxF32 x1 = center[0] - dx; NxF32 x2 = center[0] + dx; NxF32 y1 = center[1] - dy; NxF32 y2 = center[1] + dy; NxF32 z1 = center[2] - dz; NxF32 z2 = center[2] + dz; AddPoint(vcount,vertices,x1,y1,z1); AddPoint(vcount,vertices,x2,y1,z1); AddPoint(vcount,vertices,x2,y2,z1); AddPoint(vcount,vertices,x1,y2,z1); AddPoint(vcount,vertices,x1,y1,z2); AddPoint(vcount,vertices,x2,y1,z2); AddPoint(vcount,vertices,x2,y2,z2); AddPoint(vcount,vertices,x1,y2,z2); return true; // return cube } else { if ( scale ) { scale[0] = dx; scale[1] = dy; scale[2] = dz; recip[0] = 1 / dx; recip[1] = 1 / dy; recip[2] = 1 / dz; center[0]*=recip[0]; center[1]*=recip[1]; center[2]*=recip[2]; } } vtx = (const char *) svertices; for (NxU32 i=0; i dist2 ) { v[0] = px; v[1] = py; v[2] = pz; } break; } } if ( j == vcount ) { NxF32 *dest = &vertices[vcount*3]; dest[0] = px; dest[1] = py; dest[2] = pz; vcount++; } } } // ok..now make sure we didn't prune so many vertices it is now invalid. { NxF32 bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; NxF32 bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; for (NxU32 i=0; i bmax[j] ) bmax[j] = p[j]; } } NxF32 dx = bmax[0] - bmin[0]; NxF32 dy = bmax[1] - bmin[1]; NxF32 dz = bmax[2] - bmin[2]; if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3) { NxF32 cx = dx*0.5f + bmin[0]; NxF32 cy = dy*0.5f + bmin[1]; NxF32 cz = dz*0.5f + bmin[2]; NxF32 len = FLT_MAX; if ( dx >= EPSILON && dx < len ) len = dx; if ( dy >= EPSILON && dy < len ) len = dy; if ( dz >= EPSILON && dz < len ) len = dz; if ( len == FLT_MAX ) { dx = dy = dz = 0.01f; // one centimeter } else { if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge. if ( dy < EPSILON ) dy = len * 0.05f; if ( dz < EPSILON ) dz = len * 0.05f; } NxF32 x1 = cx - dx; NxF32 x2 = cx + dx; NxF32 y1 = cy - dy; NxF32 y2 = cy + dy; NxF32 z1 = cz - dz; NxF32 z2 = cz + dz; vcount = 0; // add box AddPoint(vcount,vertices,x1,y1,z1); AddPoint(vcount,vertices,x2,y1,z1); AddPoint(vcount,vertices,x2,y2,z1); AddPoint(vcount,vertices,x1,y2,z1); AddPoint(vcount,vertices,x1,y1,z2); AddPoint(vcount,vertices,x2,y1,z2); AddPoint(vcount,vertices,x2,y2,z2); AddPoint(vcount,vertices,x1,y2,z2); return true; } } return true; } void HullLibrary::BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount) { NxU32 *used = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*vcount); memset(used,0,sizeof(NxU32)*vcount); ocount = 0; for (NxU32 i=0; iConvexHullTriangle(v3,v2,v1); } //================================================================================== NxF32 HullLibrary::ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C) { NxF32 vx,vy,vz,wx,wy,wz,vw_x,vw_y,vw_z,mag; vx = (B[0] - C[0]); vy = (B[1] - C[1]); vz = (B[2] - C[2]); wx = (A[0] - B[0]); wy = (A[1] - B[1]); wz = (A[2] - B[2]); vw_x = vy * wz - vz * wy; vw_y = vz * wx - vx * wz; vw_z = vx * wy - vy * wx; 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; } n[0] = vw_x * mag; n[1] = vw_y * mag; n[2] = vw_z * mag; return mag; } }; // End of namespace