3651 lines
96 KiB
C++
3651 lines
96 KiB
C++
/*!
|
|
**
|
|
** Copyright (c) 2007 by John W. Ratcliff mailto:jratcliff@infiniplex.net
|
|
**
|
|
** 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 Phone: 636-486-4040 (let it ring a long time while it goes through switches)
|
|
** Skype ID: jratcliff63367
|
|
** Yahoo: jratcliff63367
|
|
** AOL: jratcliff1961
|
|
** email: jratcliff@infiniplex.net
|
|
** Personal website: http://jratcliffscarab.blogspot.com
|
|
** Coding Website: http://codesuppository.blogspot.com
|
|
** FundRaising Blog: http://amillionpixels.blogspot.com
|
|
** Fundraising site: http://www.amillionpixels.us
|
|
** New Temple Site: http://newtemple.blogspot.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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
|
|
#include <stdarg.h>
|
|
#include <setjmp.h>
|
|
|
|
#include "cd_hull.h"
|
|
|
|
#define STANDALONE 1 // This #define is used when tranferring this source code to other projects
|
|
|
|
#if STANDALONE
|
|
|
|
#undef NX_ALLOC
|
|
#undef NX_FREE
|
|
|
|
#define NX_ALLOC(x,y) malloc(x)
|
|
#define NX_FREE(x) free(x)
|
|
|
|
#else
|
|
#include "Allocateable.h"
|
|
#endif
|
|
|
|
namespace ConvexDecomposition
|
|
{
|
|
|
|
//*****************************************************
|
|
//*** DARRAY.H
|
|
//*****************************************************
|
|
|
|
template <class Type> class ArrayRet;
|
|
template <class Type> class Array
|
|
{
|
|
public:
|
|
Array(int s=0);
|
|
Array(Array<Type> &array);
|
|
Array(ArrayRet<Type> &array);
|
|
~Array();
|
|
void allocate(int s);
|
|
void SetSize(int s);
|
|
void Pack();
|
|
Type& Add(Type);
|
|
void AddUnique(Type);
|
|
int Contains(Type);
|
|
void Insert(Type,int);
|
|
int IndexOf(Type);
|
|
void Remove(Type);
|
|
void DelIndex(int i);
|
|
Type * element;
|
|
int count;
|
|
int array_size;
|
|
const Type &operator[](int i) const { assert(i>=0 && i<count); return element[i]; }
|
|
Type &operator[](int i) { assert(i>=0 && i<count); return element[i]; }
|
|
Type &Pop() { assert(count); count--; return element[count]; }
|
|
Array<Type> &operator=(Array<Type> &array);
|
|
Array<Type> &operator=(ArrayRet<Type> &array);
|
|
// operator ArrayRet<Type> &() { return *(ArrayRet<Type> *)this;} // this worked but i suspect could be dangerous
|
|
};
|
|
|
|
template <class Type> class ArrayRet:public Array<Type>
|
|
{
|
|
};
|
|
|
|
template <class Type> Array<Type>::Array(int s)
|
|
{
|
|
count=0;
|
|
array_size = 0;
|
|
element = NULL;
|
|
if(s)
|
|
{
|
|
allocate(s);
|
|
}
|
|
}
|
|
|
|
|
|
template <class Type> Array<Type>::Array(Array<Type> &array)
|
|
{
|
|
count=0;
|
|
array_size = 0;
|
|
element = NULL;
|
|
for(int i=0;i<array.count;i++)
|
|
{
|
|
Add(array[i]);
|
|
}
|
|
}
|
|
|
|
|
|
template <class Type> Array<Type>::Array(ArrayRet<Type> &array)
|
|
{
|
|
*this = array;
|
|
}
|
|
template <class Type> Array<Type> &Array<Type>::operator=(ArrayRet<Type> &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 <class Type> Array<Type> &Array<Type>::operator=(Array<Type> &array)
|
|
{
|
|
count=0;
|
|
for(int i=0;i<array.count;i++)
|
|
{
|
|
Add(array[i]);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class Type> Array<Type>::~Array()
|
|
{
|
|
if (element != NULL)
|
|
{
|
|
NX_FREE(element);
|
|
}
|
|
count=0;array_size=0;element=NULL;
|
|
}
|
|
|
|
template <class Type> void Array<Type>::allocate(int s)
|
|
{
|
|
assert(s>0);
|
|
assert(s>=count);
|
|
Type *old = element;
|
|
array_size =s;
|
|
element = (Type *) NX_ALLOC( sizeof(Type)*array_size, CONVEX_TEMP );
|
|
assert(element);
|
|
for(int i=0;i<count;i++)
|
|
{
|
|
element[i]=old[i];
|
|
}
|
|
if(old)
|
|
{
|
|
NX_FREE(old);
|
|
}
|
|
}
|
|
|
|
template <class Type> void Array<Type>::SetSize(int s)
|
|
{
|
|
if(s==0)
|
|
{
|
|
if(element)
|
|
{
|
|
NX_FREE(element);
|
|
element = NULL;
|
|
}
|
|
array_size = s;
|
|
}
|
|
else
|
|
{
|
|
allocate(s);
|
|
}
|
|
count=s;
|
|
}
|
|
|
|
template <class Type> void Array<Type>::Pack()
|
|
{
|
|
allocate(count);
|
|
}
|
|
|
|
template <class Type> Type& Array<Type>::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 <class Type> int Array<Type>::Contains(Type t)
|
|
{
|
|
int i;
|
|
int found=0;
|
|
for(i=0;i<count;i++)
|
|
{
|
|
if(element[i] == t) found++;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
template <class Type> void Array<Type>::AddUnique(Type t)
|
|
{
|
|
if(!Contains(t)) Add(t);
|
|
}
|
|
|
|
|
|
template <class Type> void Array<Type>::DelIndex(int i)
|
|
{
|
|
assert(i<count);
|
|
count--;
|
|
while(i<count)
|
|
{
|
|
element[i] = element[i+1];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
template <class Type> void Array<Type>::Remove(Type t)
|
|
{
|
|
int i;
|
|
for(i=0;i<count;i++)
|
|
{
|
|
if(element[i] == t)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
assert(i<count); // assert object t is in the array.
|
|
DelIndex(i);
|
|
for(i=0;i<count;i++)
|
|
{
|
|
assert(element[i] != t);
|
|
}
|
|
}
|
|
|
|
template <class Type> void Array<Type>::Insert(Type t,int k)
|
|
{
|
|
int i=count;
|
|
Add(t); // to allocate space
|
|
while(i>k)
|
|
{
|
|
element[i]=element[i-1];
|
|
i--;
|
|
}
|
|
assert(i==k);
|
|
element[k]=t;
|
|
}
|
|
|
|
|
|
template <class Type> int Array<Type>::IndexOf(Type t)
|
|
{
|
|
int i;
|
|
for(i=0;i<count;i++)
|
|
{
|
|
if(element[i] == t)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
//****************************************************
|
|
//** VECMATH.H
|
|
//****************************************************
|
|
#ifndef PLUGIN_3DSMAX
|
|
#define PI (3.1415926535897932384626433832795f)
|
|
#endif
|
|
|
|
#define DEG2RAD (PI / 180.0f)
|
|
#define RAD2DEG (180.0f / PI)
|
|
#define SQRT_OF_2 (1.4142135f)
|
|
#define OFFSET(Class,Member) (((char*) (&(((Class*)NULL)-> Member )))- ((char*)NULL))
|
|
|
|
|
|
|
|
int argmin(double a[],int n);
|
|
double sqr(double a);
|
|
double clampf(double a) ;
|
|
double Round(double a,double precision);
|
|
double Interpolate(const double &f0,const double &f1,double alpha) ;
|
|
|
|
template <class T>
|
|
void Swap(T &a,T &b)
|
|
{
|
|
T tmp = a;
|
|
a=b;
|
|
b=tmp;
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
T Max(const T &a,const T &b)
|
|
{
|
|
return (a>b)?a:b;
|
|
}
|
|
|
|
template <class T>
|
|
T Min(const T &a,const T &b)
|
|
{
|
|
return (a<b)?a:b;
|
|
}
|
|
|
|
//----------------------------------
|
|
|
|
class int3
|
|
{
|
|
public:
|
|
int x,y,z;
|
|
int3(){};
|
|
int3(int _x,int _y, int _z){x=_x;y=_y;z=_z;}
|
|
const int& operator[](int i) const {return (&x)[i];}
|
|
int& operator[](int i) {return (&x)[i];}
|
|
};
|
|
|
|
|
|
//-------- 2D --------
|
|
|
|
class double2
|
|
{
|
|
public:
|
|
double x,y;
|
|
double2(){x=0;y=0;};
|
|
double2(double _x,double _y){x=_x;y=_y;}
|
|
double& operator[](int i) {assert(i>=0&&i<2);return ((double*)this)[i];}
|
|
const double& operator[](int i) const {assert(i>=0&&i<2);return ((double*)this)[i];}
|
|
};
|
|
inline double2 operator-( const double2& a, const double2& b ){return double2(a.x-b.x,a.y-b.y);}
|
|
inline double2 operator+( const double2& a, const double2& b ){return double2(a.x+b.x,a.y+b.y);}
|
|
|
|
//--------- 3D ---------
|
|
|
|
class double3 // 3D
|
|
{
|
|
public:
|
|
double x,y,z;
|
|
double3(){x=0;y=0;z=0;};
|
|
double3(double _x,double _y,double _z){x=_x;y=_y;z=_z;};
|
|
//operator double *() { return &x;};
|
|
double& operator[](int i) {assert(i>=0&&i<3);return ((double*)this)[i];}
|
|
const double& operator[](int i) const {assert(i>=0&&i<3);return ((double*)this)[i];}
|
|
# ifdef PLUGIN_3DSMAX
|
|
double3(const Point3 &p):x(p.x),y(p.y),z(p.z){}
|
|
operator Point3(){return *((Point3*)this);}
|
|
# endif
|
|
};
|
|
|
|
|
|
double3& operator+=( double3 &a, const double3& b );
|
|
double3& operator-=( double3 &a ,const double3& b );
|
|
double3& operator*=( double3 &v ,const double s );
|
|
double3& operator/=( double3 &v, const double s );
|
|
|
|
double magnitude( const double3& v );
|
|
double3 normalize( const double3& v );
|
|
double3 safenormalize(const double3 &v);
|
|
double3 vabs(const double3 &v);
|
|
double3 operator+( const double3& a, const double3& b );
|
|
double3 operator-( const double3& a, const double3& b );
|
|
double3 operator-( const double3& v );
|
|
double3 operator*( const double3& v, const double s );
|
|
double3 operator*( const double s, const double3& v );
|
|
double3 operator/( const double3& v, const double s );
|
|
inline int operator==( const double3 &a, const double3 &b ) { return (a.x==b.x && a.y==b.y && a.z==b.z); }
|
|
inline int operator!=( const double3 &a, const double3 &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.
|
|
double dot( const double3& a, const double3& b );
|
|
double3 cmul( const double3 &a, const double3 &b);
|
|
double3 cross( const double3& a, const double3& b );
|
|
double3 Interpolate(const double3 &v0,const double3 &v1,double alpha);
|
|
double3 Round(const double3& a,double precision);
|
|
double3 VectorMax(const double3 &a, const double3 &b);
|
|
double3 VectorMin(const double3 &a, const double3 &b);
|
|
|
|
|
|
|
|
class double3x3
|
|
{
|
|
public:
|
|
double3 x,y,z; // the 3 rows of the Matrix
|
|
double3x3(){}
|
|
double3x3(double xx,double xy,double xz,double yx,double yy,double yz,double zx,double zy,double zz):x(xx,xy,xz),y(yx,yy,yz),z(zx,zy,zz){}
|
|
double3x3(double3 _x,double3 _y,double3 _z):x(_x),y(_y),z(_z){}
|
|
double3& operator[](int i) {assert(i>=0&&i<3);return (&x)[i];}
|
|
const double3& operator[](int i) const {assert(i>=0&&i<3);return (&x)[i];}
|
|
double& operator()(int r, int c) {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];}
|
|
const double& operator()(int r, int c) const {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];}
|
|
};
|
|
double3x3 Transpose( const double3x3& m );
|
|
double3 operator*( const double3& v , const double3x3& m );
|
|
double3 operator*( const double3x3& m , const double3& v );
|
|
double3x3 operator*( const double3x3& m , const double& s );
|
|
double3x3 operator*( const double3x3& ma, const double3x3& mb );
|
|
double3x3 operator/( const double3x3& a, const double& s ) ;
|
|
double3x3 operator+( const double3x3& a, const double3x3& b );
|
|
double3x3 operator-( const double3x3& a, const double3x3& b );
|
|
double3x3 &operator+=( double3x3& a, const double3x3& b );
|
|
double3x3 &operator-=( double3x3& a, const double3x3& b );
|
|
double3x3 &operator*=( double3x3& a, const double& s );
|
|
double Determinant(const double3x3& m );
|
|
double3x3 Inverse(const double3x3& a); // its just 3x3 so we simply do that cofactor method
|
|
|
|
|
|
//-------- 4D Math --------
|
|
|
|
class double4
|
|
{
|
|
public:
|
|
double x,y,z,w;
|
|
double4(){x=0;y=0;z=0;w=0;};
|
|
double4(double _x,double _y,double _z,double _w){x=_x;y=_y;z=_z;w=_w;}
|
|
double4(const double3 &v,double _w){x=v.x;y=v.y;z=v.z;w=_w;}
|
|
//operator double *() { return &x;};
|
|
double& operator[](int i) {assert(i>=0&&i<4);return ((double*)this)[i];}
|
|
const double& operator[](int i) const {assert(i>=0&&i<4);return ((double*)this)[i];}
|
|
const double3& xyz() const { return *((double3*)this);}
|
|
double3& xyz() { return *((double3*)this);}
|
|
};
|
|
|
|
|
|
struct D3DXMATRIX;
|
|
|
|
class double4x4
|
|
{
|
|
public:
|
|
double4 x,y,z,w; // the 4 rows
|
|
double4x4(){}
|
|
double4x4(const double4 &_x, const double4 &_y, const double4 &_z, const double4 &_w):x(_x),y(_y),z(_z),w(_w){}
|
|
double4x4(double m00, double m01, double m02, double m03,
|
|
double m10, double m11, double m12, double m13,
|
|
double m20, double m21, double m22, double m23,
|
|
double m30, double m31, double m32, double m33 )
|
|
:x(m00,m01,m02,m03),y(m10,m11,m12,m13),z(m20,m21,m22,m23),w(m30,m31,m32,m33){}
|
|
double& operator()(int r, int c) {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];}
|
|
const double& operator()(int r, int c) const {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];}
|
|
operator double* () {return &x.x;}
|
|
operator const double* () const {return &x.x;}
|
|
operator struct D3DXMATRIX* () { return (struct D3DXMATRIX*) this;}
|
|
operator const struct D3DXMATRIX* () const { return (struct D3DXMATRIX*) this;}
|
|
};
|
|
|
|
|
|
int operator==( const double4 &a, const double4 &b );
|
|
double4 Homogenize(const double3 &v3,const double &w=1.0f); // Turns a 3D double3 4D vector4 by appending w
|
|
double4 cmul( const double4 &a, const double4 &b);
|
|
double4 operator*( const double4 &v, double s);
|
|
double4 operator*( double s, const double4 &v);
|
|
double4 operator+( const double4 &a, const double4 &b);
|
|
double4 operator-( const double4 &a, const double4 &b);
|
|
double4x4 operator*( const double4x4& a, const double4x4& b );
|
|
double4 operator*( const double4& v, const double4x4& m );
|
|
double4x4 Inverse(const double4x4 &m);
|
|
double4x4 MatrixRigidInverse(const double4x4 &m);
|
|
double4x4 MatrixTranspose(const double4x4 &m);
|
|
double4x4 MatrixPerspectiveFov(double fovy, double Aspect, double zn, double zf );
|
|
double4x4 MatrixTranslation(const double3 &t);
|
|
double4x4 MatrixRotationZ(const double angle_radians);
|
|
double4x4 MatrixLookAt(const double3& eye, const double3& at, const double3& up);
|
|
int operator==( const double4x4 &a, const double4x4 &b );
|
|
|
|
|
|
//-------- Quaternion ------------
|
|
|
|
class Quaternion :public double4
|
|
{
|
|
public:
|
|
Quaternion() { x = y = z = 0.0f; w = 1.0f; }
|
|
Quaternion( double3 v, double t ) { v = normalize(v); w = cos(t/2.0f); v = v*sin(t/2.0f); x = v.x; y = v.y; z = v.z; }
|
|
Quaternion(double _x, double _y, double _z, double _w){x=_x;y=_y;z=_z;w=_w;}
|
|
double angle() const { return acos(w)*2.0f; }
|
|
double3 axis() const { double3 a(x,y,z); if(fabs(angle())<0.0000001f) return double3(1,0,0); return a*(1/sin(angle()/2.0f)); }
|
|
double3 xdir() const { return double3( 1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-w*y) ); }
|
|
double3 ydir() const { return double3( 2*(x*y-w*z),1-2*(x*x+z*z), 2*(y*z+w*x) ); }
|
|
double3 zdir() const { return double3( 2*(x*z+w*y), 2*(y*z-w*x),1-2*(x*x+y*y) ); }
|
|
double3x3 getmatrix() const { return double3x3( xdir(), ydir(), zdir() ); }
|
|
operator double3x3() { return getmatrix(); }
|
|
void Normalize();
|
|
};
|
|
|
|
Quaternion& operator*=(Quaternion& a, double s );
|
|
Quaternion operator*( const Quaternion& a, double s );
|
|
Quaternion operator*( const Quaternion& a, const Quaternion& b);
|
|
Quaternion operator+( const Quaternion& a, const Quaternion& b );
|
|
Quaternion normalize( Quaternion a );
|
|
double dot( const Quaternion &a, const Quaternion &b );
|
|
double3 operator*( const Quaternion& q, const double3& v );
|
|
double3 operator*( const double3& v, const Quaternion& q );
|
|
Quaternion slerp( Quaternion a, const Quaternion& b, double interp );
|
|
Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,double alpha);
|
|
Quaternion RotationArc(double3 v0, double3 v1 ); // returns quat q where q*v0=v1
|
|
Quaternion Inverse(const Quaternion &q);
|
|
double4x4 MatrixFromQuatVec(const Quaternion &q, const double3 &v);
|
|
|
|
|
|
//------ Euler Angle -----
|
|
|
|
Quaternion YawPitchRoll( double yaw, double pitch, double roll );
|
|
double Yaw( const Quaternion& q );
|
|
double Pitch( const Quaternion& q );
|
|
double Roll( Quaternion q );
|
|
double Yaw( const double3& v );
|
|
double Pitch( const double3& v );
|
|
|
|
|
|
//------- Plane ----------
|
|
|
|
class Plane
|
|
{
|
|
public:
|
|
double3 normal;
|
|
double dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0
|
|
Plane(const double3 &n,double d):normal(n),dist(d){}
|
|
Plane():normal(),dist(0){}
|
|
void Transform(const double3 &position, const Quaternion &orientation);
|
|
};
|
|
|
|
inline Plane PlaneFlip(const Plane &plane){return Plane(-plane.normal,-plane.dist);}
|
|
inline int operator==( const Plane &a, const Plane &b ) { return (a.normal==b.normal && a.dist==b.dist); }
|
|
inline int coplanar( const Plane &a, const Plane &b ) { return (a==b || a==PlaneFlip(b)); }
|
|
|
|
|
|
//--------- Utility Functions ------
|
|
|
|
double3 PlaneLineIntersection(const Plane &plane, const double3 &p0, const double3 &p1);
|
|
double3 PlaneProject(const Plane &plane, const double3 &point);
|
|
double3 LineProject(const double3 &p0, const double3 &p1, const double3 &a); // projects a onto infinite line p0p1
|
|
double LineProjectTime(const double3 &p0, const double3 &p1, const double3 &a);
|
|
double3 ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2);
|
|
int PolyHit(const double3 *vert,const int n,const double3 &v0, const double3 &v1, double3 *impact=NULL, double3 *normal=NULL);
|
|
int BoxInside(const double3 &p,const double3 &bmin, const double3 &bmax) ;
|
|
int BoxIntersect(const double3 &v0, const double3 &v1, const double3 &bmin, const double3 &bmax, double3 *impact);
|
|
double DistanceBetweenLines(const double3 &ustart, const double3 &udir, const double3 &vstart, const double3 &vdir, double3 *upoint=NULL, double3 *vpoint=NULL);
|
|
double3 TriNormal(const double3 &v0, const double3 &v1, const double3 &v2);
|
|
double3 NormalOf(const double3 *vert, const int n);
|
|
Quaternion VirtualTrackBall(const double3 &cop, const double3 &cor, const double3 &dir0, const double3 &dir1);
|
|
|
|
|
|
|
|
|
|
//*****************************************************
|
|
// ** VECMATH.CPP
|
|
//*****************************************************
|
|
|
|
|
|
double sqr(double a) {return a*a;}
|
|
double clampf(double a) {return Min(1.0,Max(0.0,a));}
|
|
|
|
|
|
double Round(double a,double precision)
|
|
{
|
|
return floor(0.5f+a/precision)*precision;
|
|
}
|
|
|
|
|
|
double Interpolate(const double &f0,const double &f1,double alpha)
|
|
{
|
|
return f0*(1-alpha) + f1*alpha;
|
|
}
|
|
|
|
|
|
int argmin(double a[],int n)
|
|
{
|
|
int r=0;
|
|
for(int i=1;i<n;i++)
|
|
{
|
|
if(a[i]<a[r])
|
|
{
|
|
r = i;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
//------------ double3 (3D) --------------
|
|
|
|
|
|
|
|
double3 operator+( const double3& a, const double3& b )
|
|
{
|
|
return double3(a.x+b.x, a.y+b.y, a.z+b.z);
|
|
}
|
|
|
|
|
|
double3 operator-( const double3& a, const double3& b )
|
|
{
|
|
return double3( a.x-b.x, a.y-b.y, a.z-b.z );
|
|
}
|
|
|
|
|
|
double3 operator-( const double3& v )
|
|
{
|
|
return double3( -v.x, -v.y, -v.z );
|
|
}
|
|
|
|
|
|
double3 operator*( const double3& v, double s )
|
|
{
|
|
return double3( v.x*s, v.y*s, v.z*s );
|
|
}
|
|
|
|
|
|
double3 operator*( double s, const double3& v )
|
|
{
|
|
return double3( v.x*s, v.y*s, v.z*s );
|
|
}
|
|
|
|
|
|
double3 operator/( const double3& v, double s )
|
|
{
|
|
return v*(1.0f/s);
|
|
}
|
|
|
|
double dot( const double3& a, const double3& b )
|
|
{
|
|
return a.x*b.x + a.y*b.y + a.z*b.z;
|
|
}
|
|
|
|
double3 cmul( const double3 &v1, const double3 &v2)
|
|
{
|
|
return double3(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
|
|
}
|
|
|
|
|
|
double3 cross( const double3& a, const double3& b )
|
|
{
|
|
return double3( a.y*b.z - a.z*b.y,
|
|
a.z*b.x - a.x*b.z,
|
|
a.x*b.y - a.y*b.x );
|
|
}
|
|
|
|
|
|
|
|
|
|
double3& operator+=( double3& a , const double3& b )
|
|
{
|
|
a.x += b.x;
|
|
a.y += b.y;
|
|
a.z += b.z;
|
|
return a;
|
|
}
|
|
|
|
|
|
double3& operator-=( double3& a , const double3& b )
|
|
{
|
|
a.x -= b.x;
|
|
a.y -= b.y;
|
|
a.z -= b.z;
|
|
return a;
|
|
}
|
|
|
|
|
|
double3& operator*=(double3& v , double s )
|
|
{
|
|
v.x *= s;
|
|
v.y *= s;
|
|
v.z *= s;
|
|
return v;
|
|
}
|
|
|
|
|
|
double3& operator/=(double3& v , double s )
|
|
{
|
|
double sinv = 1.0f / s;
|
|
v.x *= sinv;
|
|
v.y *= sinv;
|
|
v.z *= sinv;
|
|
return v;
|
|
}
|
|
|
|
double3 vabs(const double3 &v)
|
|
{
|
|
return double3(fabs(v.x),fabs(v.y),fabs(v.z));
|
|
}
|
|
|
|
|
|
double magnitude( const double3& v )
|
|
{
|
|
return sqrt(sqr(v.x) + sqr( v.y)+ sqr(v.z));
|
|
}
|
|
|
|
|
|
|
|
double3 normalize( const double3 &v )
|
|
{
|
|
// this routine, normalize, is ok, provided magnitude works!!
|
|
double d=magnitude(v);
|
|
if (d==0)
|
|
{
|
|
printf("Cant normalize ZERO vector\n");
|
|
assert(0);// yes this could go here
|
|
d=0.1f;
|
|
}
|
|
d = 1/d;
|
|
return double3(v.x*d,v.y*d,v.z*d);
|
|
}
|
|
|
|
double3 safenormalize(const double3 &v)
|
|
{
|
|
if(magnitude(v)<=0.0f)
|
|
{
|
|
return double3(1,0,0);
|
|
}
|
|
return normalize(v);
|
|
}
|
|
|
|
double3 Round(const double3 &a,double precision)
|
|
{
|
|
return double3(Round(a.x,precision),Round(a.y,precision),Round(a.z,precision));
|
|
}
|
|
|
|
|
|
double3 Interpolate(const double3 &v0,const double3 &v1,double alpha)
|
|
{
|
|
return v0*(1-alpha) + v1*alpha;
|
|
}
|
|
|
|
double3 VectorMin(const double3 &a,const double3 &b)
|
|
{
|
|
return double3(Min(a.x,b.x),Min(a.y,b.y),Min(a.z,b.z));
|
|
}
|
|
double3 VectorMax(const double3 &a,const double3 &b)
|
|
{
|
|
return double3(Max(a.x,b.x),Max(a.y,b.y),Max(a.z,b.z));
|
|
}
|
|
|
|
// the statement v1*v2 is ambiguous since there are 3 types
|
|
// of vector multiplication
|
|
// - componantwise (for example combining colors)
|
|
// - dot product
|
|
// - cross product
|
|
// Therefore we never declare/implement this function.
|
|
// So we will never see: double3 operator*(double3 a,double3 b)
|
|
|
|
|
|
|
|
|
|
//------------ double3x3 ---------------
|
|
double Determinant(const double3x3 &m)
|
|
{
|
|
return m.x.x*m.y.y*m.z.z + m.y.x*m.z.y*m.x.z + m.z.x*m.x.y*m.y.z
|
|
-m.x.x*m.z.y*m.y.z - m.y.x*m.x.y*m.z.z - m.z.x*m.y.y*m.x.z ;
|
|
}
|
|
|
|
double3x3 Inverse(const double3x3 &a)
|
|
{
|
|
double3x3 b;
|
|
double d=Determinant(a);
|
|
assert(d!=0);
|
|
for(int i=0;i<3;i++)
|
|
{
|
|
for(int j=0;j<3;j++)
|
|
{
|
|
int i1=(i+1)%3;
|
|
int i2=(i+2)%3;
|
|
int j1=(j+1)%3;
|
|
int j2=(j+2)%3;
|
|
// reverse indexs i&j to take transpose
|
|
b[j][i] = (a[i1][j1]*a[i2][j2]-a[i1][j2]*a[i2][j1])/d;
|
|
}
|
|
}
|
|
// Matrix check=a*b; // Matrix 'check' should be the identity (or close to it)
|
|
return b;
|
|
}
|
|
|
|
|
|
double3x3 Transpose( const double3x3& m )
|
|
{
|
|
return double3x3( double3(m.x.x,m.y.x,m.z.x),
|
|
double3(m.x.y,m.y.y,m.z.y),
|
|
double3(m.x.z,m.y.z,m.z.z));
|
|
}
|
|
|
|
|
|
double3 operator*(const double3& v , const double3x3 &m ) {
|
|
return double3((m.x.x*v.x + m.y.x*v.y + m.z.x*v.z),
|
|
(m.x.y*v.x + m.y.y*v.y + m.z.y*v.z),
|
|
(m.x.z*v.x + m.y.z*v.y + m.z.z*v.z));
|
|
}
|
|
double3 operator*(const double3x3 &m,const double3& v ) {
|
|
return double3(dot(m.x,v),dot(m.y,v),dot(m.z,v));
|
|
}
|
|
|
|
|
|
double3x3 operator*( const double3x3& a, const double3x3& b )
|
|
{
|
|
return double3x3(a.x*b,a.y*b,a.z*b);
|
|
}
|
|
|
|
double3x3 operator*( const double3x3& a, const double& s )
|
|
{
|
|
return double3x3(a.x*s, a.y*s ,a.z*s);
|
|
}
|
|
double3x3 operator/( const double3x3& a, const double& s )
|
|
{
|
|
double t=1/s;
|
|
return double3x3(a.x*t, a.y*t ,a.z*t);
|
|
}
|
|
double3x3 operator+( const double3x3& a, const double3x3& b )
|
|
{
|
|
return double3x3(a.x+b.x, a.y+b.y, a.z+b.z);
|
|
}
|
|
double3x3 operator-( const double3x3& a, const double3x3& b )
|
|
{
|
|
return double3x3(a.x-b.x, a.y-b.y, a.z-b.z);
|
|
}
|
|
double3x3 &operator+=( double3x3& a, const double3x3& b )
|
|
{
|
|
a.x+=b.x;
|
|
a.y+=b.y;
|
|
a.z+=b.z;
|
|
return a;
|
|
}
|
|
double3x3 &operator-=( double3x3& a, const double3x3& b )
|
|
{
|
|
a.x-=b.x;
|
|
a.y-=b.y;
|
|
a.z-=b.z;
|
|
return a;
|
|
}
|
|
double3x3 &operator*=( double3x3& a, const double& s )
|
|
{
|
|
a.x*=s;
|
|
a.y*=s;
|
|
a.z*=s;
|
|
return a;
|
|
}
|
|
|
|
|
|
|
|
double3 ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2){
|
|
double3x3 mp =Transpose(double3x3(p0.normal,p1.normal,p2.normal));
|
|
double3x3 mi = Inverse(mp);
|
|
double3 b(p0.dist,p1.dist,p2.dist);
|
|
return -b * mi;
|
|
}
|
|
|
|
|
|
//--------------- 4D ----------------
|
|
|
|
double4 operator*( const double4& v, const double4x4& m )
|
|
{
|
|
return v.x*m.x + v.y*m.y + v.z*m.z + v.w*m.w; // yes this actually works
|
|
}
|
|
|
|
int operator==( const double4 &a, const double4 &b )
|
|
{
|
|
return (a.x==b.x && a.y==b.y && a.z==b.z && a.w==b.w);
|
|
}
|
|
|
|
|
|
// Dont implement m*v for now, since that might confuse us
|
|
// All our transforms are based on multiplying the "row" vector on the left
|
|
//double4 operator*(const double4x4& m , const double4& v )
|
|
//{
|
|
// return double4(dot(v,m.x),dot(v,m.y),dot(v,m.z),dot(v,m.w));
|
|
//}
|
|
|
|
|
|
|
|
double4 cmul( const double4 &a, const double4 &b)
|
|
{
|
|
return double4(a.x*b.x,a.y*b.y,a.z*b.z,a.w*b.w);
|
|
}
|
|
|
|
|
|
double4 operator*( const double4 &v, double s)
|
|
{
|
|
return double4(v.x*s,v.y*s,v.z*s,v.w*s);
|
|
}
|
|
|
|
|
|
double4 operator*( double s, const double4 &v)
|
|
{
|
|
return double4(v.x*s,v.y*s,v.z*s,v.w*s);
|
|
}
|
|
|
|
|
|
double4 operator+( const double4 &a, const double4 &b)
|
|
{
|
|
return double4(a.x+b.x,a.y+b.y,a.z+b.z,a.w+b.w);
|
|
}
|
|
|
|
|
|
|
|
double4 operator-( const double4 &a, const double4 &b)
|
|
{
|
|
return double4(a.x-b.x,a.y-b.y,a.z-b.z,a.w-b.w);
|
|
}
|
|
|
|
|
|
double4 Homogenize(const double3 &v3,const double &w)
|
|
{
|
|
return double4(v3.x,v3.y,v3.z,w);
|
|
}
|
|
|
|
|
|
|
|
double4x4 operator*( const double4x4& a, const double4x4& b )
|
|
{
|
|
return double4x4(a.x*b,a.y*b,a.z*b,a.w*b);
|
|
}
|
|
|
|
double4x4 MatrixTranspose(const double4x4 &m)
|
|
{
|
|
return double4x4(
|
|
m.x.x, m.y.x, m.z.x, m.w.x,
|
|
m.x.y, m.y.y, m.z.y, m.w.y,
|
|
m.x.z, m.y.z, m.z.z, m.w.z,
|
|
m.x.w, m.y.w, m.z.w, m.w.w );
|
|
}
|
|
|
|
double4x4 MatrixRigidInverse(const double4x4 &m)
|
|
{
|
|
double4x4 trans_inverse = MatrixTranslation(-m.w.xyz());
|
|
double4x4 rot = m;
|
|
rot.w = double4(0,0,0,1);
|
|
return trans_inverse * MatrixTranspose(rot);
|
|
}
|
|
|
|
|
|
double4x4 MatrixPerspectiveFov(double fovy, double aspect, double zn, double zf )
|
|
{
|
|
double h = 1.0f/tan(fovy/2.0f); // view space height
|
|
double w = h / aspect ; // view space width
|
|
return double4x4(
|
|
w, 0, 0 , 0,
|
|
0, h, 0 , 0,
|
|
0, 0, zf/(zn-zf) , -1,
|
|
0, 0, zn*zf/(zn-zf) , 0 );
|
|
}
|
|
|
|
|
|
|
|
double4x4 MatrixLookAt(const double3& eye, const double3& at, const double3& up)
|
|
{
|
|
double4x4 m;
|
|
m.w.w = 1.0f;
|
|
m.w.xyz() = eye;
|
|
m.z.xyz() = normalize(eye-at);
|
|
m.x.xyz() = normalize(cross(up,m.z.xyz()));
|
|
m.y.xyz() = cross(m.z.xyz(),m.x.xyz());
|
|
return MatrixRigidInverse(m);
|
|
}
|
|
|
|
|
|
double4x4 MatrixTranslation(const double3 &t)
|
|
{
|
|
return double4x4(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
t.x,t.y,t.z,1 );
|
|
}
|
|
|
|
|
|
double4x4 MatrixRotationZ(const double angle_radians)
|
|
{
|
|
double s = sin(angle_radians);
|
|
double c = cos(angle_radians);
|
|
return double4x4(
|
|
c, s, 0, 0,
|
|
-s, c, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1 );
|
|
}
|
|
|
|
|
|
|
|
int operator==( const double4x4 &a, const double4x4 &b )
|
|
{
|
|
return (a.x==b.x && a.y==b.y && a.z==b.z && a.w==b.w);
|
|
}
|
|
|
|
|
|
double4x4 Inverse(const double4x4 &m)
|
|
{
|
|
double4x4 d;
|
|
double *dst = &d.x.x;
|
|
double tmp[12]; /* temp array for pairs */
|
|
double src[16]; /* array of transpose source matrix */
|
|
double det; /* determinant */
|
|
/* transpose matrix */
|
|
for ( int i = 0; i < 4; i++) {
|
|
src[i] = m(i,0) ;
|
|
src[i + 4] = m(i,1);
|
|
src[i + 8] = m(i,2);
|
|
src[i + 12] = m(i,3);
|
|
}
|
|
/* calculate pairs for first 8 elements (cofactors) */
|
|
tmp[0] = src[10] * src[15];
|
|
tmp[1] = src[11] * src[14];
|
|
tmp[2] = src[9] * src[15];
|
|
tmp[3] = src[11] * src[13];
|
|
tmp[4] = src[9] * src[14];
|
|
tmp[5] = src[10] * src[13];
|
|
tmp[6] = src[8] * src[15];
|
|
tmp[7] = src[11] * src[12];
|
|
tmp[8] = src[8] * src[14];
|
|
tmp[9] = src[10] * src[12];
|
|
tmp[10] = src[8] * src[13];
|
|
tmp[11] = src[9] * src[12];
|
|
/* calculate first 8 elements (cofactors) */
|
|
dst[0] = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7];
|
|
dst[0] -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7];
|
|
dst[1] = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7];
|
|
dst[1] -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7];
|
|
dst[2] = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7];
|
|
dst[2] -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7];
|
|
dst[3] = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6];
|
|
dst[3] -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6];
|
|
dst[4] = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3];
|
|
dst[4] -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3];
|
|
dst[5] = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3];
|
|
dst[5] -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3];
|
|
dst[6] = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3];
|
|
dst[6] -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3];
|
|
dst[7] = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2];
|
|
dst[7] -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2];
|
|
/* calculate pairs for second 8 elements (cofactors) */
|
|
tmp[0] = src[2]*src[7];
|
|
tmp[1] = src[3]*src[6];
|
|
tmp[2] = src[1]*src[7];
|
|
tmp[3] = src[3]*src[5];
|
|
tmp[4] = src[1]*src[6];
|
|
tmp[5] = src[2]*src[5];
|
|
tmp[6] = src[0]*src[7];
|
|
tmp[7] = src[3]*src[4];
|
|
tmp[8] = src[0]*src[6];
|
|
tmp[9] = src[2]*src[4];
|
|
tmp[10] = src[0]*src[5];
|
|
tmp[11] = src[1]*src[4];
|
|
/* calculate second 8 elements (cofactors) */
|
|
dst[8] = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15];
|
|
dst[8] -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15];
|
|
dst[9] = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15];
|
|
dst[9] -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15];
|
|
dst[10] = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15];
|
|
dst[10]-= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15];
|
|
dst[11] = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14];
|
|
dst[11]-= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14];
|
|
dst[12] = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9];
|
|
dst[12]-= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10];
|
|
dst[13] = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10];
|
|
dst[13]-= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8];
|
|
dst[14] = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8];
|
|
dst[14]-= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9];
|
|
dst[15] = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9];
|
|
dst[15]-= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8];
|
|
/* calculate determinant */
|
|
det=src[0]*dst[0]+src[1]*dst[1]+src[2]*dst[2]+src[3]*dst[3];
|
|
/* calculate matrix inverse */
|
|
det = 1/det;
|
|
for ( int j = 0; j < 16; j++)
|
|
dst[j] *= det;
|
|
return d;
|
|
}
|
|
|
|
|
|
//--------- Quaternion --------------
|
|
|
|
Quaternion operator*( const Quaternion& a, const Quaternion& b )
|
|
{
|
|
Quaternion c;
|
|
c.w = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
|
|
c.x = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
|
|
c.y = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
|
|
c.z = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
|
|
return c;
|
|
}
|
|
|
|
|
|
Quaternion operator*( const Quaternion& a, double b )
|
|
{
|
|
return Quaternion(a.x*b, a.y*b, a.z*b ,a.w*b);
|
|
}
|
|
|
|
Quaternion Inverse(const Quaternion &q)
|
|
{
|
|
return Quaternion(-q.x,-q.y,-q.z,q.w);
|
|
}
|
|
|
|
Quaternion& operator*=( Quaternion& q, const double s )
|
|
{
|
|
q.x *= s;
|
|
q.y *= s;
|
|
q.z *= s;
|
|
q.w *= s;
|
|
return q;
|
|
}
|
|
void Quaternion::Normalize()
|
|
{
|
|
double m = sqrt(sqr(w)+sqr(x)+sqr(y)+sqr(z));
|
|
if(m<0.000000001f) {
|
|
w=1.0f;
|
|
x=y=z=0.0f;
|
|
return;
|
|
}
|
|
(*this) *= (1.0f/m);
|
|
}
|
|
|
|
double3 operator*( const Quaternion& q, const double3& v )
|
|
{
|
|
// The following is equivalent to:
|
|
//return (q.getmatrix() * v);
|
|
double qx2 = q.x*q.x;
|
|
double qy2 = q.y*q.y;
|
|
double qz2 = q.z*q.z;
|
|
|
|
double qxqy = q.x*q.y;
|
|
double qxqz = q.x*q.z;
|
|
double qxqw = q.x*q.w;
|
|
double qyqz = q.y*q.z;
|
|
double qyqw = q.y*q.w;
|
|
double qzqw = q.z*q.w;
|
|
return double3(
|
|
(1-2*(qy2+qz2))*v.x + (2*(qxqy-qzqw))*v.y + (2*(qxqz+qyqw))*v.z ,
|
|
(2*(qxqy+qzqw))*v.x + (1-2*(qx2+qz2))*v.y + (2*(qyqz-qxqw))*v.z ,
|
|
(2*(qxqz-qyqw))*v.x + (2*(qyqz+qxqw))*v.y + (1-2*(qx2+qy2))*v.z );
|
|
}
|
|
|
|
double3 operator*( const double3& v, const Quaternion& q )
|
|
{
|
|
assert(0); // must multiply with the quat on the left
|
|
return double3(0.0f,0.0f,0.0f);
|
|
}
|
|
|
|
Quaternion operator+( const Quaternion& a, const Quaternion& b )
|
|
{
|
|
return Quaternion(a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w);
|
|
}
|
|
|
|
double dot( const Quaternion &a,const Quaternion &b )
|
|
{
|
|
return (a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z);
|
|
}
|
|
|
|
Quaternion normalize( Quaternion a )
|
|
{
|
|
double m = sqrt(sqr(a.w)+sqr(a.x)+sqr(a.y)+sqr(a.z));
|
|
if(m<0.000000001)
|
|
{
|
|
a.w=1;
|
|
a.x=a.y=a.z=0;
|
|
return a;
|
|
}
|
|
return a * (1/m);
|
|
}
|
|
|
|
Quaternion slerp( Quaternion a, const Quaternion& b, double interp )
|
|
{
|
|
if(dot(a,b) <0.0)
|
|
{
|
|
a.w=-a.w;
|
|
a.x=-a.x;
|
|
a.y=-a.y;
|
|
a.z=-a.z;
|
|
}
|
|
double d = dot(a,b);
|
|
if(d>=1.0) {
|
|
return a;
|
|
}
|
|
double theta = acos(d);
|
|
if(theta==0.0f) { return(a);}
|
|
return a*(sin(theta-interp*theta)/sin(theta)) + b*(sin(interp*theta)/sin(theta));
|
|
}
|
|
|
|
|
|
Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,double alpha) {
|
|
return slerp(q0,q1,alpha);
|
|
}
|
|
|
|
|
|
Quaternion YawPitchRoll( double yaw, double pitch, double roll )
|
|
{
|
|
roll *= DEG2RAD;
|
|
yaw *= DEG2RAD;
|
|
pitch *= DEG2RAD;
|
|
return Quaternion(double3(0.0f,0.0f,1.0f),yaw)*Quaternion(double3(1.0f,0.0f,0.0f),pitch)*Quaternion(double3(0.0f,1.0f,0.0f),roll);
|
|
}
|
|
|
|
double Yaw( const Quaternion& q )
|
|
{
|
|
static double3 v;
|
|
v=q.ydir();
|
|
return (v.y==0.0&&v.x==0.0) ? 0.0: atan2(-v.x,v.y)*RAD2DEG;
|
|
}
|
|
|
|
double Pitch( const Quaternion& q )
|
|
{
|
|
static double3 v;
|
|
v=q.ydir();
|
|
return atan2(v.z,sqrt(sqr(v.x)+sqr(v.y)))*RAD2DEG;
|
|
}
|
|
|
|
double Roll( Quaternion q )
|
|
{
|
|
q = Quaternion(double3(0.0f,0.0f,1.0f),-Yaw(q)*DEG2RAD) *q;
|
|
q = Quaternion(double3(1.0f,0.0f,0.0f),-Pitch(q)*DEG2RAD) *q;
|
|
return atan2(-q.xdir().z,q.xdir().x)*RAD2DEG;
|
|
}
|
|
|
|
double Yaw( const double3& v )
|
|
{
|
|
return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2(-v.x,v.y)*RAD2DEG;
|
|
}
|
|
|
|
double Pitch( const double3& v )
|
|
{
|
|
return atan2(v.z,sqrt(sqr(v.x)+sqr(v.y)))*RAD2DEG;
|
|
}
|
|
|
|
|
|
//------------- Plane --------------
|
|
|
|
|
|
void Plane::Transform(const double3 &position, const Quaternion &orientation) {
|
|
// Transforms the plane to the space defined by the
|
|
// given position/orientation.
|
|
static double3 newnormal;
|
|
static double3 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(double3 v0,double3 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?
|
|
double3 c = cross(v0,v1);
|
|
double d = dot(v0,v1);
|
|
if(d<=-1.0f) { return Quaternion(1,0,0,0);} // 180 about x axis
|
|
double s = 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;
|
|
}
|
|
|
|
|
|
double4x4 MatrixFromQuatVec(const Quaternion &q, const double3 &v)
|
|
{
|
|
// builds a 4x4 transformation matrix based on orientation q and translation v
|
|
double qx2 = q.x*q.x;
|
|
double qy2 = q.y*q.y;
|
|
double qz2 = q.z*q.z;
|
|
|
|
double qxqy = q.x*q.y;
|
|
double qxqz = q.x*q.z;
|
|
double qxqw = q.x*q.w;
|
|
double qyqz = q.y*q.z;
|
|
double qyqw = q.y*q.w;
|
|
double qzqw = q.z*q.w;
|
|
|
|
return double4x4(
|
|
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 );
|
|
}
|
|
|
|
|
|
double3 PlaneLineIntersection(const Plane &plane, const double3 &p0, const double3 &p1)
|
|
{
|
|
// returns the point where the line p0-p1 intersects the plane n&d
|
|
static double3 dif;
|
|
dif = p1-p0;
|
|
double dn= dot(plane.normal,dif);
|
|
double t = -(plane.dist+dot(plane.normal,p0) )/dn;
|
|
return p0 + (dif*t);
|
|
}
|
|
|
|
double3 PlaneProject(const Plane &plane, const double3 &point)
|
|
{
|
|
return point - plane.normal * (dot(point,plane.normal)+plane.dist);
|
|
}
|
|
|
|
double3 LineProject(const double3 &p0, const double3 &p1, const double3 &a)
|
|
{
|
|
double3 w;
|
|
w = p1-p0;
|
|
double t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z));
|
|
return p0+ w*t;
|
|
}
|
|
|
|
|
|
double LineProjectTime(const double3 &p0, const double3 &p1, const double3 &a)
|
|
{
|
|
double3 w;
|
|
w = p1-p0;
|
|
double t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z));
|
|
return t;
|
|
}
|
|
|
|
|
|
|
|
double3 TriNormal(const double3 &v0, const double3 &v1, const double3 &v2)
|
|
{
|
|
// return the normal of the triangle
|
|
// inscribed by v0, v1, and v2
|
|
double3 cp=cross(v1-v0,v2-v1);
|
|
double m=magnitude(cp);
|
|
if(m==0) return double3(1,0,0);
|
|
return cp*(1.0f/m);
|
|
}
|
|
|
|
|
|
|
|
int BoxInside(const double3 &p, const double3 &bmin, const double3 &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 );
|
|
}
|
|
|
|
|
|
int BoxIntersect(const double3 &v0, const double3 &v1, const double3 &bmin, const double3 &bmax,double3 *impact)
|
|
{
|
|
if(BoxInside(v0,bmin,bmax))
|
|
{
|
|
*impact=v0;
|
|
return 1;
|
|
}
|
|
if(v0.x<=bmin.x && v1.x>=bmin.x)
|
|
{
|
|
double a = (bmin.x-v0.x)/(v1.x-v0.x);
|
|
//v.x = bmin.x;
|
|
double vy = (1-a) *v0.y + a*v1.y;
|
|
double 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)
|
|
{
|
|
double a = (bmax.x-v0.x)/(v1.x-v0.x);
|
|
//v.x = bmax.x;
|
|
double vy = (1-a) *v0.y + a*v1.y;
|
|
double 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)
|
|
{
|
|
double a = (bmin.y-v0.y)/(v1.y-v0.y);
|
|
double vx = (1-a) *v0.x + a*v1.x;
|
|
//v.y = bmin.y;
|
|
double 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)
|
|
{
|
|
double a = (bmax.y-v0.y)/(v1.y-v0.y);
|
|
double vx = (1-a) *v0.x + a*v1.x;
|
|
// vy = bmax.y;
|
|
double 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)
|
|
{
|
|
double a = (bmin.z-v0.z)/(v1.z-v0.z);
|
|
double vx = (1-a) *v0.x + a*v1.x;
|
|
double 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)
|
|
{
|
|
double a = (bmax.z-v0.z)/(v1.z-v0.z);
|
|
double vx = (1-a) *v0.x + a*v1.x;
|
|
double 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;
|
|
}
|
|
|
|
|
|
double DistanceBetweenLines(const double3 &ustart, const double3 &udir, const double3 &vstart, const double3 &vdir, double3 *upoint, double3 *vpoint)
|
|
{
|
|
static double3 cp;
|
|
cp = normalize(cross(udir,vdir));
|
|
|
|
double distu = -dot(cp,ustart);
|
|
double distv = -dot(cp,vstart);
|
|
double dist = (double)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 double3 &cop, const double3 &cor, const double3 &dir1, const double3 &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.
|
|
double m;
|
|
// compute plane
|
|
double3 nrml = cor - cop;
|
|
double fudgefactor = 1.0f/(magnitude(nrml) * 0.25f); // since trackball proportional to distance from cop
|
|
nrml = normalize(nrml);
|
|
double dist = -dot(nrml,cor);
|
|
double3 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 * sqrt(1-m*m));
|
|
}
|
|
double3 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 * sqrt(1-m*m));
|
|
}
|
|
return RotationArc(u,v);
|
|
}
|
|
|
|
|
|
int countpolyhit=0;
|
|
int PolyHit(const double3 *vert, const int n, const double3 &v0, const double3 &v1, double3 *impact, double3 *normal)
|
|
{
|
|
countpolyhit++;
|
|
int i;
|
|
double3 nrml(0,0,0);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
int i1=(i+1)%n;
|
|
int i2=(i+2)%n;
|
|
nrml = nrml + cross(vert[i1]-vert[i],vert[i2]-vert[i1]);
|
|
}
|
|
|
|
double m = magnitude(nrml);
|
|
if(m==0.0)
|
|
{
|
|
return 0;
|
|
}
|
|
nrml = nrml * (1.0f/m);
|
|
double dist = -dot(nrml,vert[0]);
|
|
double d0,d1;
|
|
if((d0=dot(v0,nrml)+dist) <0 || (d1=dot(v1,nrml)+dist) >0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static double3 the_point;
|
|
// By using the cached plane distances d0 and d1
|
|
// we can optimize the following:
|
|
// the_point = planelineintersection(nrml,dist,v0,v1);
|
|
double a = d0/(d0-d1);
|
|
the_point = v0*(1-a) + v1*a;
|
|
|
|
|
|
int inside=1;
|
|
for(int j=0;inside && j<n;j++)
|
|
{
|
|
// let inside = 0 if outside
|
|
double3 pp1,pp2,side;
|
|
pp1 = vert[j] ;
|
|
pp2 = vert[(j+1)%n];
|
|
side = cross((pp2-pp1),(the_point-pp1));
|
|
inside = (dot(nrml,side) >= 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;
|
|
}
|
|
|
|
unsigned int mVcount;
|
|
unsigned int mIndexCount;
|
|
unsigned int mFaceCount;
|
|
double *mVertices;
|
|
unsigned int *mIndices;
|
|
};
|
|
|
|
bool ComputeHull(unsigned int vcount,const double *vertices,PHullResult &result,unsigned int maxverts,double inflate);
|
|
void ReleaseHull(PHullResult &result);
|
|
|
|
//*****************************************************
|
|
// HULL.cpp source code goes here
|
|
//*****************************************************
|
|
|
|
|
|
#define REAL3 double3
|
|
#define REAL double
|
|
|
|
#define COPLANAR (0)
|
|
#define UNDER (1)
|
|
#define OVER (2)
|
|
#define SPLIT (OVER|UNDER)
|
|
#define PAPERWIDTH (0.001f)
|
|
#define VOLUME_EPSILON (1e-20f)
|
|
|
|
double planetestepsilon = PAPERWIDTH;
|
|
|
|
#if STANDALONE
|
|
class ConvexH
|
|
#else
|
|
class ConvexH : public NxFoundation::NxAllocateable
|
|
#endif
|
|
{
|
|
public:
|
|
class HalfEdge
|
|
{
|
|
public:
|
|
short ea; // the other half of the edge (index into edges list)
|
|
unsigned char v; // the vertex at the start of this edge (index into vertices list)
|
|
unsigned char p; // the facet on which this edge lies (index into facets list)
|
|
HalfEdge(){}
|
|
HalfEdge(short _ea,unsigned char _v, unsigned char _p):ea(_ea),v(_v),p(_p){}
|
|
};
|
|
Array<REAL3> vertices;
|
|
Array<HalfEdge> edges;
|
|
Array<Plane> facets;
|
|
ConvexH(int vertices_size,int edges_size,int facets_size);
|
|
};
|
|
|
|
typedef ConvexH::HalfEdge HalfEdge;
|
|
|
|
ConvexH::ConvexH(int vertices_size,int edges_size,int 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)
|
|
{
|
|
#if STANDALONE
|
|
ConvexH *dst = new ConvexH(src->vertices.count,src->edges.count,src->facets.count);
|
|
#else
|
|
ConvexH *dst = NX_NEW_MEM(ConvexH(src->vertices.count,src->edges.count,src->facets.count), CONVEX_TEMP);
|
|
#endif
|
|
|
|
memcpy(dst->vertices.element,src->vertices.element,sizeof(double3)*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;
|
|
}
|
|
|
|
|
|
int PlaneTest(const Plane &p, const REAL3 &v) {
|
|
REAL a = dot(v,p.normal)+p.dist;
|
|
int flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR);
|
|
return flag;
|
|
}
|
|
|
|
int SplitTest(ConvexH &convex,const Plane &plane) {
|
|
int flag=0;
|
|
for(int i=0;i<convex.vertices.count;i++) {
|
|
flag |= PlaneTest(plane,convex.vertices[i]);
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
class VertFlag
|
|
{
|
|
public:
|
|
unsigned char planetest;
|
|
unsigned char junk;
|
|
unsigned char undermap;
|
|
unsigned char overmap;
|
|
};
|
|
class EdgeFlag
|
|
{
|
|
public:
|
|
unsigned char planetest;
|
|
unsigned char fixes;
|
|
short undermap;
|
|
short overmap;
|
|
};
|
|
class PlaneFlag
|
|
{
|
|
public:
|
|
unsigned char undermap;
|
|
unsigned char overmap;
|
|
};
|
|
class Coplanar{
|
|
public:
|
|
unsigned short ea;
|
|
unsigned char v0;
|
|
unsigned char v1;
|
|
};
|
|
|
|
int AssertIntact(ConvexH &convex) {
|
|
int i;
|
|
int estart=0;
|
|
for(i=0;i<convex.edges.count;i++) {
|
|
if(convex.edges[estart].p!= convex.edges[i].p) {
|
|
estart=i;
|
|
}
|
|
int inext = i+1;
|
|
if(inext>= convex.edges.count || convex.edges[inext].p != convex.edges[i].p) {
|
|
inext = estart;
|
|
}
|
|
assert(convex.edges[inext].p == convex.edges[i].p);
|
|
HalfEdge &edge = convex.edges[i];
|
|
int 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;i++) {
|
|
assert(COPLANAR==PlaneTest(convex.facets[convex.edges[i].p],convex.vertices[convex.edges[i].v]));
|
|
if(COPLANAR!=PlaneTest(convex.facets[convex.edges[i].p],convex.vertices[convex.edges[i].v])) return 0;
|
|
if(convex.edges[estart].p!= convex.edges[i].p) {
|
|
estart=i;
|
|
}
|
|
int i1 = i+1;
|
|
if(i1>= convex.edges.count || convex.edges[i1].p != convex.edges[i].p) {
|
|
i1 = estart;
|
|
}
|
|
int 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;
|
|
}
|
|
|
|
// back to back quads
|
|
ConvexH *test_btbq()
|
|
{
|
|
|
|
#if STANDALONE
|
|
ConvexH *convex = new ConvexH(4,8,2);
|
|
#else
|
|
ConvexH *convex = NX_NEW_MEM(ConvexH(4,8,2), CONVEX_TEMP);
|
|
#endif
|
|
|
|
convex->vertices[0] = REAL3(0,0,0);
|
|
convex->vertices[1] = REAL3(1,0,0);
|
|
convex->vertices[2] = REAL3(1,1,0);
|
|
convex->vertices[3] = REAL3(0,1,0);
|
|
convex->facets[0] = Plane(REAL3(0,0,1),0);
|
|
convex->facets[1] = Plane(REAL3(0,0,-1),0);
|
|
convex->edges[0] = HalfEdge(7,0,0);
|
|
convex->edges[1] = HalfEdge(6,1,0);
|
|
convex->edges[2] = HalfEdge(5,2,0);
|
|
convex->edges[3] = HalfEdge(4,3,0);
|
|
|
|
convex->edges[4] = HalfEdge(3,0,1);
|
|
convex->edges[5] = HalfEdge(2,3,1);
|
|
convex->edges[6] = HalfEdge(1,2,1);
|
|
convex->edges[7] = HalfEdge(0,1,1);
|
|
AssertIntact(*convex);
|
|
return convex;
|
|
}
|
|
|
|
ConvexH *test_cube()
|
|
{
|
|
#if STANDALONE
|
|
ConvexH *convex = new ConvexH(8,24,6);
|
|
#else
|
|
ConvexH *convex = NX_NEW_MEM(ConvexH(8,24,6), CONVEX_TEMP);
|
|
#endif
|
|
convex->vertices[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;
|
|
}
|
|
ConvexH *ConvexHCrop(ConvexH &convex,const Plane &slice)
|
|
{
|
|
int i;
|
|
int vertcountunder=0;
|
|
int vertcountover =0;
|
|
int edgecountunder=0;
|
|
int edgecountover =0;
|
|
int planecountunder=0;
|
|
int planecountover =0;
|
|
static Array<int> vertscoplanar; // existing vertex members of convex that are coplanar
|
|
vertscoplanar.count=0;
|
|
static Array<int> 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];
|
|
int coplanaredges_num=0;
|
|
|
|
Array<REAL3> createdverts;
|
|
// do the side-of-plane tests
|
|
for(i=0;i<convex.vertices.count;i++) {
|
|
vertflag[i].planetest = PlaneTest(slice,convex.vertices[i]);
|
|
if(vertflag[i].planetest == COPLANAR) {
|
|
// ? vertscoplanar.Add(i);
|
|
vertflag[i].undermap = vertcountunder++;
|
|
vertflag[i].overmap = vertcountover++;
|
|
}
|
|
else if(vertflag[i].planetest == UNDER) {
|
|
vertflag[i].undermap = vertcountunder++;
|
|
}
|
|
else {
|
|
assert(vertflag[i].planetest == OVER);
|
|
vertflag[i].overmap = vertcountover++;
|
|
vertflag[i].undermap = (unsigned char)-1; // for debugging purposes
|
|
}
|
|
}
|
|
int vertcountunderold = vertcountunder; // for debugging only
|
|
|
|
int under_edge_count =0;
|
|
int underplanescount=0;
|
|
int e0=0;
|
|
|
|
for(int currentplane=0; currentplane<convex.facets.count; currentplane++) {
|
|
int estart =e0;
|
|
int enextface;
|
|
int planeside = 0;
|
|
int e1 = e0+1;
|
|
int eus=-1;
|
|
int ecop=-1;
|
|
int vout=-1;
|
|
int vin =-1;
|
|
int coplanaredge = -1;
|
|
do{
|
|
|
|
if(e1 >= 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 = under_edge_count;
|
|
tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap;
|
|
tmpunderedges[under_edge_count].p = 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 = 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
|
|
int 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 = under_edge_count;
|
|
tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap;
|
|
tmpunderedges[under_edge_count].p = 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 = under_edge_count;
|
|
tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap;
|
|
tmpunderedges[under_edge_count].p = 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 = 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 = vout;
|
|
tmpunderedges[under_edge_count].p = 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
|
|
int k=estart;
|
|
assert(edge0.p == currentplane);
|
|
while(!(planeside&UNDER) && k<convex.edges.count && convex.edges[k].p==edge0.p) {
|
|
planeside |= vertflag[convex.edges[k].v].planetest;
|
|
k++;
|
|
}
|
|
if(planeside&UNDER){
|
|
tmpunderedges[under_edge_count].v = vout;
|
|
tmpunderedges[under_edge_count].p = underplanescount;
|
|
tmpunderedges[under_edge_count].ea = -1;
|
|
coplanaredge = under_edge_count; // hmmm should make a note of the edge # for later on
|
|
under_edge_count++;
|
|
|
|
}
|
|
}
|
|
else if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == UNDER) {
|
|
// first is over next is under
|
|
// new vertex!!!
|
|
if (vin!=-1) return NULL;
|
|
if(e0<edge0.ea) {
|
|
Plane &p0 = convex.facets[edge0.p];
|
|
Plane &pa = convex.facets[edgea.p];
|
|
createdverts.Add(ThreePlaneIntersection(p0,pa,slice));
|
|
//createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]));
|
|
//createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])));
|
|
vin = vertcountunder++;
|
|
}
|
|
else {
|
|
// find the new vertex that was created by edge[edge0.ea]
|
|
int nea = edgeflag[edge0.ea].undermap;
|
|
assert(tmpunderedges[nea].p==tmpunderedges[nea+1].p);
|
|
vin = tmpunderedges[nea+1].v;
|
|
assert(vin < vertcountunder);
|
|
assert(vin >= vertcountunderold); // for debugging only
|
|
}
|
|
if(vout!=-1) {
|
|
// we previously processed an edge where we went over
|
|
// now we know vin too
|
|
// ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!!
|
|
}
|
|
// output edge
|
|
tmpunderedges[under_edge_count].v = vin;
|
|
tmpunderedges[under_edge_count].p = underplanescount;
|
|
edgeflag[e0].undermap = under_edge_count;
|
|
if(e0>edge0.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 = 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 = 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 = coplanaredge;
|
|
coplanaredges[coplanaredges_num].v0 = vin;
|
|
coplanaredges[coplanaredges_num].v1 = vout;
|
|
coplanaredges_num++;
|
|
}
|
|
}
|
|
|
|
// add the new plane to the mix:
|
|
if(coplanaredges_num>0) {
|
|
tmpunderplanes[underplanescount++]=slice;
|
|
}
|
|
for(i=0;i<coplanaredges_num-1;i++) {
|
|
if(coplanaredges[i].v1 != coplanaredges[i+1].v0) {
|
|
int j = 0;
|
|
for(j=i+2;j<coplanaredges_num;j++) {
|
|
if(coplanaredges[i].v1 == coplanaredges[j].v0) {
|
|
Coplanar tmp = coplanaredges[i+1];
|
|
coplanaredges[i+1] = coplanaredges[j];
|
|
coplanaredges[j] = tmp;
|
|
break;
|
|
}
|
|
}
|
|
if(j>=coplanaredges_num)
|
|
{
|
|
// assert(j<coplanaredges_num);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
#if STANDALONE
|
|
ConvexH *punder = new ConvexH(vertcountunder,under_edge_count+coplanaredges_num,underplanescount);
|
|
#else
|
|
ConvexH *punder = NX_NEW_MEM(ConvexH(vertcountunder,under_edge_count+coplanaredges_num,underplanescount), CONVEX_TEMP);
|
|
#endif
|
|
|
|
ConvexH &under = *punder;
|
|
int k=0;
|
|
for(i=0;i<convex.vertices.count;i++) {
|
|
if(vertflag[i].planetest != OVER){
|
|
under.vertices[k++] = convex.vertices[i];
|
|
}
|
|
}
|
|
i=0;
|
|
while(k<vertcountunder) {
|
|
under.vertices[k++] = createdverts[i++];
|
|
}
|
|
assert(i==createdverts.count);
|
|
|
|
for(i=0;i<coplanaredges_num;i++) {
|
|
under.edges[under_edge_count+i].p = underplanescount-1;
|
|
under.edges[under_edge_count+i].ea = coplanaredges[i].ea;
|
|
tmpunderedges[coplanaredges[i].ea].ea = under_edge_count+i;
|
|
under.edges[under_edge_count+i].v = coplanaredges[i].v0;
|
|
}
|
|
|
|
memcpy(under.edges.element,tmpunderedges,sizeof(HalfEdge)*under_edge_count);
|
|
memcpy(under.facets.element,tmpunderplanes,sizeof(Plane)*underplanescount);
|
|
return punder;
|
|
}
|
|
|
|
|
|
double minadjangle = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other.
|
|
static int candidateplane(Plane *planes,int planes_count,ConvexH *convex,double epsilon)
|
|
{
|
|
int p =-1;
|
|
REAL md=0;
|
|
int i,j;
|
|
double maxdot_minang = cos(DEG2RAD*minadjangle);
|
|
for(i=0;i<planes_count;i++)
|
|
{
|
|
double d=0;
|
|
double dmax=0;
|
|
double dmin=0;
|
|
for(j=0;j<convex->vertices.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);
|
|
}
|
|
double dr = dmax-dmin;
|
|
if(dr<planetestepsilon) dr=1.0f; // shouldn't happen.
|
|
d = dmax /dr;
|
|
if(d<=md) continue;
|
|
for(j=0;j<convex->facets.count;j++)
|
|
{
|
|
if(planes[i]==convex->facets[j])
|
|
{
|
|
d=0;continue;
|
|
}
|
|
if(dot(planes[i].normal,convex->facets[j].normal)>maxdot_minang)
|
|
{
|
|
for(int k=0;k<convex->edges.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<class T>
|
|
inline int maxdir(const T *p,int count,const T &dir)
|
|
{
|
|
assert(count);
|
|
int m=0;
|
|
for(int i=1;i<count;i++)
|
|
{
|
|
if(dot(p[i],dir)>dot(p[m],dir)) m=i;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int maxdirfiltered(const T *p,int count,const T &dir,Array<int> &allow)
|
|
{
|
|
assert(count);
|
|
int m=-1;
|
|
for(int i=0;i<count;i++) if(allow[i])
|
|
{
|
|
if(m==-1 || dot(p[i],dir)>dot(p[m],dir)) m=i;
|
|
}
|
|
assert(m!=-1);
|
|
return m;
|
|
}
|
|
|
|
double3 orth(const double3 &v)
|
|
{
|
|
double3 a=cross(v,double3(0,0,1));
|
|
double3 b=cross(v,double3(0,1,0));
|
|
return normalize((magnitude(a)>magnitude(b))?a:b);
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int maxdirsterid(const T *p,int count,const T &dir,Array<int> &allow)
|
|
{
|
|
int 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);
|
|
int ma=-1;
|
|
for(double x = 0.0f ; x<= 360.0f ; x+= 45.0f)
|
|
{
|
|
double s = sin(DEG2RAD*(x));
|
|
double c = cos(DEG2RAD*(x));
|
|
int 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
|
|
{
|
|
int mc = ma;
|
|
for(double xx = x-40.0f ; xx <= x ; xx+= 5.0f)
|
|
{
|
|
double s = sin(DEG2RAD*(xx));
|
|
double c = cos(DEG2RAD*(xx));
|
|
int 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;
|
|
}
|
|
|
|
|
|
|
|
|
|
int operator ==(const int3 &a,const int3 &b)
|
|
{
|
|
for(int i=0;i<3;i++)
|
|
{
|
|
if(a[i]!=b[i]) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int3 roll3(int3 a)
|
|
{
|
|
int tmp=a[0];
|
|
a[0]=a[1];
|
|
a[1]=a[2];
|
|
a[2]=tmp;
|
|
return a;
|
|
}
|
|
int isa(const int3 &a,const int3 &b)
|
|
{
|
|
return ( a==b || roll3(a)==b || a==roll3(b) );
|
|
}
|
|
int b2b(const int3 &a,const int3 &b)
|
|
{
|
|
return isa(a,int3(b[2],b[1],b[0]));
|
|
}
|
|
int above(double3* vertices,const int3& t, const double3 &p, double epsilon)
|
|
{
|
|
double3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]);
|
|
return (dot(n,p-vertices[t[0]]) > epsilon); // EPSILON???
|
|
}
|
|
int hasedge(const int3 &t, int a,int b)
|
|
{
|
|
for(int i=0;i<3;i++)
|
|
{
|
|
int i1= (i+1)%3;
|
|
if(t[i]==a && t[i1]==b) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
int hasvert(const int3 &t, int v)
|
|
{
|
|
return (t[0]==v || t[1]==v || t[2]==v) ;
|
|
}
|
|
int shareedge(const int3 &a,const int3 &b)
|
|
{
|
|
int i;
|
|
for(i=0;i<3;i++)
|
|
{
|
|
int i1= (i+1)%3;
|
|
if(hasedge(a,b[i1],b[i])) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
class Tri;
|
|
|
|
static Array<Tri*> tris; // djs: For heaven's sake!!!!
|
|
|
|
#if STANDALONE
|
|
class Tri : public int3
|
|
#else
|
|
class Tri : public int3, public NxFoundation::NxAllocateable
|
|
#endif
|
|
{
|
|
public:
|
|
int3 n;
|
|
int id;
|
|
int vmax;
|
|
double rise;
|
|
Tri(int a,int b,int 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;
|
|
}
|
|
int &neib(int a,int b);
|
|
};
|
|
|
|
|
|
int &Tri::neib(int a,int b)
|
|
{
|
|
static int er=-1;
|
|
int i;
|
|
for(i=0;i<3;i++)
|
|
{
|
|
int i1=(i+1)%3;
|
|
int 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)
|
|
{
|
|
int i;
|
|
for(i=0;i<3;i++)
|
|
{
|
|
int i1=(i+1)%3;
|
|
int i2=(i+2)%3;
|
|
int a = (*s)[i1];
|
|
int 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 checkit(Tri *t)
|
|
{
|
|
int i;
|
|
assert(tris[t->id]==t);
|
|
for(i=0;i<3;i++)
|
|
{
|
|
int i1=(i+1)%3;
|
|
int i2=(i+2)%3;
|
|
int a = (*t)[i1];
|
|
int b = (*t)[i2];
|
|
assert(a!=b);
|
|
assert( tris[t->n[i]]->neib(b,a) == t->id);
|
|
}
|
|
}
|
|
void extrude(Tri *t0,int v)
|
|
{
|
|
int3 t= *t0;
|
|
int n = tris.count;
|
|
#if STANDALONE
|
|
Tri* ta = new Tri(v,t[1],t[2]);
|
|
#else
|
|
Tri* ta = NX_NEW_MEM(Tri(v,t[1],t[2]), CONVEX_TEMP);
|
|
#endif
|
|
ta->n = int3(t0->n[0],n+1,n+2);
|
|
tris[t0->n[0]]->neib(t[1],t[2]) = n+0;
|
|
#if STANDALONE
|
|
Tri* tb = new Tri(v,t[2],t[0]);
|
|
#else
|
|
Tri* tb = NX_NEW_MEM(Tri(v,t[2],t[0]), CONVEX_TEMP);
|
|
#endif
|
|
tb->n = int3(t0->n[1],n+2,n+0);
|
|
tris[t0->n[1]]->neib(t[2],t[0]) = n+1;
|
|
#if STANDALONE
|
|
Tri* tc = new Tri(v,t[0],t[1]);
|
|
#else
|
|
Tri* tc = NX_NEW_MEM(Tri(v,t[0],t[1]), CONVEX_TEMP);
|
|
#endif
|
|
tc->n = int3(t0->n[2],n+0,n+1);
|
|
tris[t0->n[2]]->neib(t[0],t[1]) = n+2;
|
|
checkit(ta);
|
|
checkit(tb);
|
|
checkit(tc);
|
|
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(double epsilon)
|
|
{
|
|
int i;
|
|
Tri *t=NULL;
|
|
for(i=0;i<tris.count;i++)
|
|
{
|
|
if(!t || (tris[i] && t->rise<tris[i]->rise))
|
|
{
|
|
t = tris[i];
|
|
}
|
|
}
|
|
return (t->rise >epsilon)?t:NULL ;
|
|
}
|
|
|
|
class int4
|
|
{
|
|
public:
|
|
int x,y,z,w;
|
|
int4(){};
|
|
int4(int _x,int _y, int _z,int _w){x=_x;y=_y;z=_z;w=_w;}
|
|
const int& operator[](int i) const {return (&x)[i];}
|
|
int& operator[](int i) {return (&x)[i];}
|
|
};
|
|
|
|
|
|
|
|
bool hasVolume(double3 *verts, int p0, int p1, int p2, int p3)
|
|
{
|
|
double3 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;
|
|
double 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(double3 *verts,int verts_count,Array<int> &allow)
|
|
{
|
|
double3 basis[3];
|
|
basis[0] = double3( 0.01f, 0.02f, 1.0f );
|
|
int p0 = maxdirsterid(verts,verts_count, basis[0],allow);
|
|
int p1 = maxdirsterid(verts,verts_count,-basis[0],allow);
|
|
basis[0] = verts[p0]-verts[p1];
|
|
if(p0==p1 || basis[0]==double3(0,0,0))
|
|
return int4(-1,-1,-1,-1);
|
|
basis[1] = cross(double3( 1, 0.02f, 0),basis[0]);
|
|
basis[2] = cross(double3(-0.02f, 1, 0),basis[0]);
|
|
basis[1] = normalize( (magnitude(basis[1])>magnitude(basis[2])) ? basis[1]:basis[2]);
|
|
int 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]));
|
|
int 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);
|
|
}
|
|
|
|
int calchullgen(double3 *verts,int verts_count, int vlimit)
|
|
{
|
|
if(verts_count <4) return 0;
|
|
if(vlimit==0) vlimit=1000000000;
|
|
int j;
|
|
double3 bmin(*verts),bmax(*verts);
|
|
Array<int> isextreme(verts_count);
|
|
Array<int> allow(verts_count);
|
|
for(j=0;j<verts_count;j++)
|
|
{
|
|
allow.Add(1);
|
|
isextreme.Add(0);
|
|
bmin = VectorMin(bmin,verts[j]);
|
|
bmax = VectorMax(bmax,verts[j]);
|
|
}
|
|
double epsilon = magnitude(bmax-bmin) * 0.001f;
|
|
|
|
|
|
int4 p = FindSimplex(verts,verts_count,allow);
|
|
if(p.x==-1) return 0; // simplex failed
|
|
|
|
|
|
|
|
double3 center = (verts[p[0]]+verts[p[1]]+verts[p[2]]+verts[p[3]]) /4.0f; // a valid interior point
|
|
#if STANDALONE
|
|
Tri *t0 = new Tri(p[2],p[3],p[1]); t0->n=int3(2,3,1);
|
|
Tri *t1 = new Tri(p[3],p[2],p[0]); t1->n=int3(3,2,0);
|
|
Tri *t2 = new Tri(p[0],p[1],p[3]); t2->n=int3(0,1,3);
|
|
Tri *t3 = new Tri(p[1],p[0],p[2]); t3->n=int3(1,0,2);
|
|
#else
|
|
Tri *t0 = NX_NEW_MEM(Tri(p[2],p[3],p[1]); t0->n=int3(2,3,1), CONVEX_TEMP);
|
|
Tri *t1 = NX_NEW_MEM(Tri(p[3],p[2],p[0]); t1->n=int3(3,2,0), CONVEX_TEMP);
|
|
Tri *t2 = NX_NEW_MEM(Tri(p[0],p[1],p[3]); t2->n=int3(0,1,3), CONVEX_TEMP);
|
|
Tri *t3 = NX_NEW_MEM(Tri(p[1],p[0],p[2]); t3->n=int3(1,0,2), CONVEX_TEMP);
|
|
#endif
|
|
isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1;
|
|
checkit(t0);checkit(t1);checkit(t2);checkit(t3);
|
|
|
|
for(j=0;j<tris.count;j++)
|
|
{
|
|
Tri *t=tris[j];
|
|
assert(t);
|
|
assert(t->vmax<0);
|
|
double3 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;
|
|
int 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;
|
|
int newstart=j;
|
|
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->id<j);
|
|
extrude(nb,v);
|
|
j=tris.count;
|
|
}
|
|
}
|
|
j=tris.count;
|
|
while(j--)
|
|
{
|
|
Tri *t=tris[j];
|
|
if(!t) continue;
|
|
if(t->vmax>=0) break;
|
|
double3 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;
|
|
}
|
|
|
|
int calchull(double3 *verts,int verts_count, int *&tris_out, int &tris_count,int vlimit)
|
|
{
|
|
int rc=calchullgen(verts,verts_count, vlimit) ;
|
|
if(!rc) return 0;
|
|
Array<int> ts;
|
|
for(int i=0;i<tris.count;i++)if(tris[i])
|
|
{
|
|
for(int j=0;j<3;j++)ts.Add((*tris[i])[j]);
|
|
delete tris[i];
|
|
}
|
|
tris_count = ts.count/3;
|
|
tris_out = ts.element;
|
|
ts.element=NULL; ts.count=ts.array_size=0;
|
|
// please reset here, otherwise, we get a nice virtual function call (R6025) error with NxCooking library
|
|
tris.SetSize( 0 );
|
|
return 1;
|
|
}
|
|
|
|
static double area2(const double3 &v0,const double3 &v1,const double3 &v2)
|
|
{
|
|
double3 cp = cross(v0-v1,v2-v0);
|
|
return dot(cp,cp);
|
|
}
|
|
int calchullpbev(double3 *verts,int verts_count,int vlimit, Array<Plane> &planes,double bevangle)
|
|
{
|
|
int i,j;
|
|
Array<Plane> bplanes;
|
|
planes.count=0;
|
|
int rc = calchullgen(verts,verts_count,vlimit);
|
|
if(!rc) return 0;
|
|
extern double minadjangle; // default is 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other.
|
|
double maxdot_minang = cos(DEG2RAD*minadjangle);
|
|
for(i=0;i<tris.count;i++)if(tris[i])
|
|
{
|
|
Plane p;
|
|
Tri *t = tris[i];
|
|
p.normal = TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
|
|
p.dist = -dot(p.normal, verts[(*t)[0]]);
|
|
for(j=0;j<3;j++)
|
|
{
|
|
if(t->n[j]<t->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;i<tris.count;i++)if(tris[i])for(j=i+1;j<tris.count;j++)if(tris[i] && tris[j])
|
|
{
|
|
Tri *ti = tris[i];
|
|
Tri *tj = tris[j];
|
|
REAL3 ni = TriNormal(verts[(*ti)[0]],verts[(*ti)[1]],verts[(*ti)[2]]);
|
|
REAL3 nj = TriNormal(verts[(*tj)[0]],verts[(*tj)[1]],verts[(*tj)[2]]);
|
|
if(dot(ni,nj)>maxdot_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]; tris[i]=NULL;
|
|
}
|
|
else
|
|
{
|
|
delete tris[j]; tris[j]=NULL;
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<tris.count;i++)if(tris[i])
|
|
{
|
|
Plane p;
|
|
Tri *t = tris[i];
|
|
p.normal = TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
|
|
p.dist = -dot(p.normal, verts[(*t)[0]]);
|
|
planes.Add(p);
|
|
}
|
|
for(i=0;i<bplanes.count;i++)
|
|
{
|
|
for(j=0;j<planes.count;j++)
|
|
{
|
|
if(dot(bplanes[i].normal,planes[j].normal)>maxdot_minang) break;
|
|
}
|
|
if(j==planes.count)
|
|
{
|
|
planes.Add(bplanes[i]);
|
|
}
|
|
}
|
|
for(i=0;i<tris.count;i++)if(tris[i])
|
|
{
|
|
delete tris[i];
|
|
}
|
|
tris.count = 0; //bad place to do the tris.SetSize(0) fix, this line is executed many times, and will result in a whole lot of allocations if the array is totally cleared here
|
|
return 1;
|
|
}
|
|
|
|
static int overhull(Plane *planes,int planes_count,double3 *verts, int verts_count,int maxplanes,
|
|
double3 *&verts_out, int &verts_count_out, int *&faces_out, int &faces_count_out ,double inflate)
|
|
{
|
|
int i,j;
|
|
if(verts_count <4) return NULL;
|
|
maxplanes = Min(maxplanes,planes_count);
|
|
double3 bmin(verts[0]),bmax(verts[0]);
|
|
for(i=0;i<verts_count;i++)
|
|
{
|
|
bmin = VectorMin(bmin,verts[i]);
|
|
bmax = VectorMax(bmax,verts[i]);
|
|
}
|
|
double diameter = magnitude(bmax-bmin);
|
|
// inflate *=diameter; // RELATIVE INFLATION
|
|
bmin -= double3(inflate*2.5f,inflate*2.5f,inflate*2.5f);
|
|
bmax += double3(inflate*2.5f,inflate*2.5f,inflate*2.5f);
|
|
// 2 is from the formula:
|
|
// D = d*|n1+n2|/(1-n1 dot n2), where d is "inflate" and
|
|
// n1 and n2 are the normals of two planes at bevelAngle to each other
|
|
// for 120 degrees, D is 2d
|
|
|
|
//bmin -= double3(inflate,inflate,inflate);
|
|
//bmax += double3(inflate,inflate,inflate);
|
|
for(i=0;i<planes_count;i++)
|
|
{
|
|
planes[i].dist -= inflate;
|
|
}
|
|
double3 emin = bmin; // VectorMin(bmin,double3(0,0,0));
|
|
double3 emax = bmax; // VectorMax(bmax,double3(0,0,0));
|
|
double epsilon = 0.01f; // size of object is taken into account within candidate plane function. Used to multiply here by magnitude(emax-emin)
|
|
planetestepsilon = magnitude(emax-emin) * PAPERWIDTH;
|
|
// todo: add bounding cube planes to force bevel. or try instead not adding the diameter expansion ??? must think.
|
|
// ConvexH *convex = ConvexHMakeCube(bmin - double3(diameter,diameter,diameter),bmax+double3(diameter,diameter,diameter));
|
|
double maxdot_minang = cos(DEG2RAD*minadjangle);
|
|
for(j=0;j<6;j++)
|
|
{
|
|
double3 n(0,0,0);
|
|
n[j/2] = (j%2)? 1.0f : -1.0f;
|
|
for(i=0;i<planes_count;i++)
|
|
{
|
|
if(dot(n,planes[i].normal)> maxdot_minang)
|
|
{
|
|
(*((j%2)?&bmax:&bmin)) += n * (diameter*0.5f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ConvexH *c = ConvexHMakeCube(REAL3(bmin),REAL3(bmax));
|
|
int 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 = (int*)NX_ALLOC(sizeof(int)*(1+c->facets.count+c->edges.count), CONVEX_TEMP); // new int[1+c->facets.count+c->edges.count];
|
|
faces_count_out=0;
|
|
i=0;
|
|
faces_out[faces_count_out++]=-1;
|
|
k=0;
|
|
while(i<c->edges.count)
|
|
{
|
|
j=1;
|
|
while(j+i<c->edges.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 double3[c->vertices.count];
|
|
verts_count_out = c->vertices.count;
|
|
for(i=0;i<c->vertices.count;i++)
|
|
{
|
|
verts_out[i] = double3(c->vertices[i]);
|
|
}
|
|
c->vertices.count=c->vertices.array_size=0; c->vertices.element=NULL;
|
|
delete c;
|
|
return 1;
|
|
}
|
|
|
|
static int overhullv(double3 *verts, int verts_count,int maxplanes,
|
|
double3 *&verts_out, int &verts_count_out, int *&faces_out, int &faces_count_out ,double inflate,double bevangle,int vlimit)
|
|
{
|
|
if(!verts_count) return 0;
|
|
extern int calchullpbev(double3 *verts,int verts_count,int vlimit, Array<Plane> &planes,double bevangle) ;
|
|
Array<Plane> planes;
|
|
int 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(unsigned int vcount,const double *vertices,PHullResult &result,unsigned int vlimit,double inflate)
|
|
{
|
|
|
|
int index_count;
|
|
int *faces;
|
|
double3 *verts_out;
|
|
int verts_count_out;
|
|
|
|
if(inflate==0.0f)
|
|
{
|
|
int *tris_out;
|
|
int tris_count;
|
|
int ret = calchull( (double3 *) vertices, (int) vcount, tris_out, tris_count, vlimit );
|
|
if(!ret) return false;
|
|
result.mIndexCount = (unsigned int) (tris_count*3);
|
|
result.mFaceCount = (unsigned int) tris_count;
|
|
result.mVertices = (double*) vertices;
|
|
result.mVcount = (unsigned int) vcount;
|
|
result.mIndices = (unsigned int *) tris_out;
|
|
return true;
|
|
}
|
|
|
|
int ret = overhullv((double3*)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<int3> tris;
|
|
int n=faces[0];
|
|
int k=1;
|
|
for(int i=0;i<n;i++)
|
|
{
|
|
int pn = faces[k++];
|
|
for(int j=2;j<pn;j++) tris.Add(int3(faces[k],faces[k+j-1],faces[k+j]));
|
|
k+=pn;
|
|
}
|
|
assert(tris.count == index_count-1-(n*3));
|
|
NX_FREE(faces); // PT: I added that. Is it ok ?
|
|
|
|
result.mIndexCount = (unsigned int) (tris.count*3);
|
|
result.mFaceCount = (unsigned int) tris.count;
|
|
result.mVertices = (double*) verts_out;
|
|
result.mVcount = (unsigned int) verts_count_out;
|
|
result.mIndices = (unsigned int *) tris.element;
|
|
tris.element=NULL; tris.count = tris.array_size=0;
|
|
ConvexDecomposition::tris.SetSize(0); //have to set the size to 0 in order to protect from a "pure virtual function call" problem
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void ReleaseHull(PHullResult &result)
|
|
{
|
|
NX_FREE(result.mIndices); // PT: I added that. Is it ok ?
|
|
NX_FREE(result.mVertices); // PT: I added that. Is it ok ?
|
|
result.mVcount = 0;
|
|
result.mIndexCount = 0;
|
|
result.mIndices = 0;
|
|
result.mVertices = 0;
|
|
result.mIndices = 0;
|
|
}
|
|
|
|
|
|
|
|
//****** HULLLIB source code
|
|
|
|
|
|
HullError HullLibrary::CreateConvexHull(const HullDesc &desc, // describes the input request
|
|
HullResult &result) // contains the resulst
|
|
{
|
|
HullError ret = QE_FAIL;
|
|
|
|
|
|
PHullResult hr;
|
|
|
|
unsigned int vcount = desc.mVcount;
|
|
if ( vcount < 8 ) vcount = 8;
|
|
|
|
double *vsource = (double *) NX_ALLOC( sizeof(double)*vcount*3, CONVEX_TEMP );
|
|
|
|
|
|
double scale[3];
|
|
|
|
unsigned int ovcount;
|
|
|
|
bool ok = CleanupVertices(desc.mVcount,desc.mVertices, desc.mVertexStride, ovcount, vsource, desc.mNormalEpsilon, scale ); // normalize point cloud, remove duplicates!
|
|
|
|
if ( ok )
|
|
{
|
|
double bmin[3];
|
|
double bmax[3];
|
|
|
|
|
|
if ( 1 ) // scale vertices back to their original size.
|
|
{
|
|
|
|
for (unsigned int i=0; i<ovcount; i++)
|
|
{
|
|
double *v = &vsource[i*3];
|
|
v[0]*=scale[0];
|
|
v[1]*=scale[1];
|
|
v[2]*=scale[2];
|
|
|
|
if ( i == 0 )
|
|
{
|
|
bmin[0] = bmax[0] = v[0];
|
|
bmin[1] = bmax[1] = v[1];
|
|
bmin[2] = bmax[2] = v[2];
|
|
}
|
|
else
|
|
{
|
|
if ( v[0] < bmin[0] ) bmin[0] = v[0];
|
|
if ( v[1] < bmin[1] ) bmin[1] = v[1];
|
|
if ( v[2] < bmin[2] ) bmin[2] = v[2];
|
|
if ( v[0] > bmax[0] ) bmax[0] = v[0];
|
|
if ( v[1] > bmax[1] ) bmax[1] = v[1];
|
|
if ( v[2] > bmax[2] ) bmax[2] = v[2];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
double skinwidth = 0;
|
|
|
|
if ( desc.HasHullFlag(QF_SKIN_WIDTH) )
|
|
{
|
|
skinwidth = desc.mSkinWidth;
|
|
if ( skinwidth < 0 ) // if it is a negative skinwidth we shrink the hull points relative to the center.
|
|
{
|
|
double center[3];
|
|
|
|
center[0] = (bmax[0] - bmin[0])*0.5f + bmin[0];
|
|
center[1] = (bmax[1] - bmin[1])*0.5f + bmin[1];
|
|
center[2] = (bmax[2] - bmin[2])*0.5f + bmin[2];
|
|
|
|
double dx = (bmax[0]-bmin[0])*0.5f;
|
|
double dy = (bmax[1]-bmin[1])*0.5f;
|
|
double dz = (bmax[2]-bmin[2])*0.5f;
|
|
double dist = sqrt(dx*dx+dy*dy+dz*dz);
|
|
|
|
skinwidth*=-1; // make it positive...
|
|
|
|
double scale = 1.0f - (skinwidth/dist);
|
|
if ( scale < 0.3f ) scale = 0.3f;
|
|
for (unsigned int i=0; i<ovcount; i++)
|
|
{
|
|
double *v = &vsource[i*3];
|
|
|
|
v[0]-=center[0];
|
|
v[1]-=center[1];
|
|
v[2]-=center[2];
|
|
|
|
v[0]*=scale;
|
|
v[1]*=scale;
|
|
v[2]*=scale;
|
|
|
|
v[0]+=center[0];
|
|
v[1]+=center[1];
|
|
v[2]+=center[2];
|
|
}
|
|
skinwidth = 0;
|
|
}
|
|
}
|
|
|
|
ok = ComputeHull(ovcount,vsource,hr,desc.mMaxVertices,skinwidth);
|
|
|
|
if ( ok )
|
|
{
|
|
|
|
// re-index triangle mesh so it refers to only used vertices, rebuild a new vertex table.
|
|
double *vscratch = (double *) NX_ALLOC( sizeof(double)*hr.mVcount*3, CONVEX_TEMP );
|
|
BringOutYourDead(hr.mVertices,hr.mVcount, vscratch, ovcount, hr.mIndices, hr.mIndexCount );
|
|
|
|
ret = QE_OK;
|
|
|
|
if ( desc.HasHullFlag(QF_TRIANGLES) ) // if he wants the results as triangle!
|
|
{
|
|
result.mPolygons = false;
|
|
result.mNumOutputVertices = ovcount;
|
|
result.mOutputVertices = (double *)NX_ALLOC( sizeof(double)*ovcount*3, CONVEX_TEMP );
|
|
result.mNumFaces = hr.mFaceCount;
|
|
result.mNumIndices = hr.mIndexCount;
|
|
|
|
result.mIndices = (unsigned int *) NX_ALLOC( sizeof(unsigned int)*hr.mIndexCount, CONVEX_TEMP );
|
|
|
|
memcpy(result.mOutputVertices, vscratch, sizeof(double)*3*ovcount );
|
|
|
|
if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
|
|
{
|
|
|
|
const unsigned int *source = hr.mIndices;
|
|
unsigned int *dest = result.mIndices;
|
|
|
|
for (unsigned int i=0; i<hr.mFaceCount; i++)
|
|
{
|
|
dest[0] = source[2];
|
|
dest[1] = source[1];
|
|
dest[2] = source[0];
|
|
dest+=3;
|
|
source+=3;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
memcpy(result.mIndices, hr.mIndices, sizeof(unsigned int)*hr.mIndexCount);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.mPolygons = true;
|
|
result.mNumOutputVertices = ovcount;
|
|
result.mOutputVertices = (double *)NX_ALLOC( sizeof(double)*ovcount*3, CONVEX_TEMP );
|
|
result.mNumFaces = hr.mFaceCount;
|
|
result.mNumIndices = hr.mIndexCount+hr.mFaceCount;
|
|
result.mIndices = (unsigned int *) NX_ALLOC( sizeof(unsigned int)*result.mNumIndices, CONVEX_TEMP );
|
|
memcpy(result.mOutputVertices, vscratch, sizeof(double)*3*ovcount );
|
|
|
|
if ( 1 )
|
|
{
|
|
const unsigned int *source = hr.mIndices;
|
|
unsigned int *dest = result.mIndices;
|
|
for (unsigned int i=0; i<hr.mFaceCount; i++)
|
|
{
|
|
dest[0] = 3;
|
|
if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
|
|
{
|
|
dest[1] = source[2];
|
|
dest[2] = source[1];
|
|
dest[3] = source[0];
|
|
}
|
|
else
|
|
{
|
|
dest[1] = source[0];
|
|
dest[2] = source[1];
|
|
dest[3] = source[2];
|
|
}
|
|
|
|
dest+=4;
|
|
source+=3;
|
|
}
|
|
}
|
|
}
|
|
// ReleaseHull frees memory for hr.mVertices, which can be the
|
|
// same pointer as vsource, so be sure to set it to NULL if necessary
|
|
if ( hr.mVertices == vsource) vsource = NULL;
|
|
|
|
ReleaseHull(hr);
|
|
|
|
if ( vscratch )
|
|
{
|
|
NX_FREE(vscratch);
|
|
}
|
|
}
|
|
}
|
|
|
|
// this pointer is usually freed in ReleaseHull()
|
|
if ( vsource )
|
|
{
|
|
NX_FREE(vsource);
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
HullError HullLibrary::ReleaseResult(HullResult &result) // release memory allocated for this result, we are done with it.
|
|
{
|
|
if ( result.mOutputVertices )
|
|
{
|
|
NX_FREE(result.mOutputVertices);
|
|
result.mOutputVertices = 0;
|
|
}
|
|
if ( result.mIndices )
|
|
{
|
|
NX_FREE(result.mIndices);
|
|
result.mIndices = 0;
|
|
}
|
|
return QE_OK;
|
|
}
|
|
|
|
|
|
static void AddPoint(unsigned int &vcount,double *p,double x,double y,double z)
|
|
{
|
|
double *dest = &p[vcount*3];
|
|
dest[0] = x;
|
|
dest[1] = y;
|
|
dest[2] = z;
|
|
vcount++;
|
|
}
|
|
|
|
|
|
double GetDist(double px,double py,double pz,const double *p2)
|
|
{
|
|
|
|
double dx = px - p2[0];
|
|
double dy = py - p2[1];
|
|
double dz = pz - p2[2];
|
|
|
|
return dx*dx+dy*dy+dz*dz;
|
|
}
|
|
|
|
|
|
|
|
bool HullLibrary::CleanupVertices(unsigned int svcount,
|
|
const double *svertices,
|
|
unsigned int stride,
|
|
unsigned int &vcount, // output number of vertices
|
|
double *vertices, // location to store the results.
|
|
double normalepsilon,
|
|
double *scale)
|
|
{
|
|
if ( svcount == 0 ) return false;
|
|
|
|
|
|
#define EPSILON 0.000001f // close enough to consider two doubleing point numbers to be 'the same'.
|
|
|
|
bool ret = false;
|
|
|
|
vcount = 0;
|
|
|
|
double recip[3];
|
|
|
|
if ( scale )
|
|
{
|
|
scale[0] = 1;
|
|
scale[1] = 1;
|
|
scale[2] = 1;
|
|
}
|
|
|
|
double bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
double bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
|
|
const char *vtx = (const char *) svertices;
|
|
|
|
if ( 1 )
|
|
{
|
|
for (unsigned int i=0; i<svcount; i++)
|
|
{
|
|
const double *p = (const double *) vtx;
|
|
|
|
vtx+=stride;
|
|
|
|
for (int j=0; j<3; j++)
|
|
{
|
|
if ( p[j] < bmin[j] ) bmin[j] = p[j];
|
|
if ( p[j] > bmax[j] ) bmax[j] = p[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
double dx = bmax[0] - bmin[0];
|
|
double dy = bmax[1] - bmin[1];
|
|
double dz = bmax[2] - bmin[2];
|
|
|
|
double 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 )
|
|
{
|
|
|
|
double 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;
|
|
}
|
|
|
|
double x1 = center[0] - dx;
|
|
double x2 = center[0] + dx;
|
|
|
|
double y1 = center[1] - dy;
|
|
double y2 = center[1] + dy;
|
|
|
|
double z1 = center[2] - dz;
|
|
double 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 (unsigned int i=0; i<svcount; i++)
|
|
{
|
|
|
|
const double *p = (const double *)vtx;
|
|
vtx+=stride;
|
|
|
|
double px = p[0];
|
|
double py = p[1];
|
|
double pz = p[2];
|
|
|
|
if ( scale )
|
|
{
|
|
px = px*recip[0]; // normalize
|
|
py = py*recip[1]; // normalize
|
|
pz = pz*recip[2]; // normalize
|
|
}
|
|
|
|
if ( 1 )
|
|
{
|
|
unsigned int j;
|
|
|
|
for (j=0; j<vcount; j++)
|
|
{
|
|
double *v = &vertices[j*3];
|
|
|
|
double x = v[0];
|
|
double y = v[1];
|
|
double z = v[2];
|
|
|
|
double dx = fabs(x - px );
|
|
double dy = fabs(y - py );
|
|
double dz = fabs(z - pz );
|
|
|
|
if ( dx < normalepsilon && dy < normalepsilon && dz < normalepsilon )
|
|
{
|
|
// ok, it is close enough to the old one
|
|
// now let us see if it is further from the center of the point cloud than the one we already recorded.
|
|
// in which case we keep this one instead.
|
|
|
|
double dist1 = GetDist(px,py,pz,center);
|
|
double dist2 = GetDist(v[0],v[1],v[2],center);
|
|
|
|
if ( dist1 > dist2 )
|
|
{
|
|
v[0] = px;
|
|
v[1] = py;
|
|
v[2] = pz;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j == vcount )
|
|
{
|
|
double *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.
|
|
if ( 1 )
|
|
{
|
|
double bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
double bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
|
|
for (unsigned int i=0; i<vcount; i++)
|
|
{
|
|
const double *p = &vertices[i*3];
|
|
for (int j=0; j<3; j++)
|
|
{
|
|
if ( p[j] < bmin[j] ) bmin[j] = p[j];
|
|
if ( p[j] > bmax[j] ) bmax[j] = p[j];
|
|
}
|
|
}
|
|
|
|
double dx = bmax[0] - bmin[0];
|
|
double dy = bmax[1] - bmin[1];
|
|
double dz = bmax[2] - bmin[2];
|
|
|
|
if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3)
|
|
{
|
|
double cx = dx*0.5f + bmin[0];
|
|
double cy = dy*0.5f + bmin[1];
|
|
double cz = dz*0.5f + bmin[2];
|
|
|
|
double 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;
|
|
}
|
|
|
|
double x1 = cx - dx;
|
|
double x2 = cx + dx;
|
|
|
|
double y1 = cy - dy;
|
|
double y2 = cy + dy;
|
|
|
|
double z1 = cz - dz;
|
|
double 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 double *verts,unsigned int vcount, double *overts,unsigned int &ocount,unsigned int *indices,unsigned indexcount)
|
|
{
|
|
unsigned int *used = (unsigned int *)NX_ALLOC(sizeof(unsigned int)*vcount, CONVEX_TEMP );
|
|
memset(used,0,sizeof(unsigned int)*vcount);
|
|
|
|
ocount = 0;
|
|
|
|
for (unsigned int i=0; i<indexcount; i++)
|
|
{
|
|
unsigned int v = indices[i]; // original array index
|
|
|
|
assert( v >= 0 && v < vcount );
|
|
|
|
if ( used[v] ) // if already remapped
|
|
{
|
|
indices[i] = used[v]-1; // index to new array
|
|
}
|
|
else
|
|
{
|
|
|
|
indices[i] = ocount; // new index mapping
|
|
|
|
overts[ocount*3+0] = verts[v*3+0]; // copy old vert to new vert array
|
|
overts[ocount*3+1] = verts[v*3+1];
|
|
overts[ocount*3+2] = verts[v*3+2];
|
|
|
|
ocount++; // increment output vert count
|
|
|
|
assert( ocount >=0 && ocount <= vcount );
|
|
|
|
used[v] = ocount; // assign new index remapping
|
|
}
|
|
}
|
|
|
|
NX_FREE(used);
|
|
}
|
|
|
|
|
|
//==================================================================================
|
|
HullError HullLibrary::CreateTriangleMesh(HullResult &answer,ConvexHullTriangleInterface *iface)
|
|
{
|
|
HullError ret = QE_FAIL;
|
|
|
|
|
|
const double *p = answer.mOutputVertices;
|
|
const unsigned int *idx = answer.mIndices;
|
|
unsigned int fcount = answer.mNumFaces;
|
|
|
|
if ( p && idx && fcount )
|
|
{
|
|
ret = QE_OK;
|
|
|
|
for (unsigned int i=0; i<fcount; i++)
|
|
{
|
|
unsigned int pcount = *idx++;
|
|
|
|
unsigned int i1 = *idx++;
|
|
unsigned int i2 = *idx++;
|
|
unsigned int i3 = *idx++;
|
|
|
|
const double *p1 = &p[i1*3];
|
|
const double *p2 = &p[i2*3];
|
|
const double *p3 = &p[i3*3];
|
|
|
|
AddConvexTriangle(iface,p1,p2,p3);
|
|
|
|
pcount-=3;
|
|
while ( pcount )
|
|
{
|
|
i3 = *idx++;
|
|
p2 = p3;
|
|
p3 = &p[i3*3];
|
|
|
|
AddConvexTriangle(iface,p1,p2,p3);
|
|
pcount--;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//==================================================================================
|
|
void HullLibrary::AddConvexTriangle(ConvexHullTriangleInterface *callback,const double *p1,const double *p2,const double *p3)
|
|
{
|
|
ConvexHullVertex v1,v2,v3;
|
|
|
|
#define TSCALE1 (1.0f/4.0f)
|
|
|
|
v1.mPos[0] = p1[0];
|
|
v1.mPos[1] = p1[1];
|
|
v1.mPos[2] = p1[2];
|
|
|
|
v2.mPos[0] = p2[0];
|
|
v2.mPos[1] = p2[1];
|
|
v2.mPos[2] = p2[2];
|
|
|
|
v3.mPos[0] = p3[0];
|
|
v3.mPos[1] = p3[1];
|
|
v3.mPos[2] = p3[2];
|
|
|
|
double n[3];
|
|
ComputeNormal(n,p1,p2,p3);
|
|
|
|
v1.mNormal[0] = n[0];
|
|
v1.mNormal[1] = n[1];
|
|
v1.mNormal[2] = n[2];
|
|
|
|
v2.mNormal[0] = n[0];
|
|
v2.mNormal[1] = n[1];
|
|
v2.mNormal[2] = n[2];
|
|
|
|
v3.mNormal[0] = n[0];
|
|
v3.mNormal[1] = n[1];
|
|
v3.mNormal[2] = n[2];
|
|
|
|
const double *tp1 = p1;
|
|
const double *tp2 = p2;
|
|
const double *tp3 = p3;
|
|
|
|
int i1 = 0;
|
|
int i2 = 0;
|
|
|
|
double nx = fabs(n[0]);
|
|
double ny = fabs(n[1]);
|
|
double nz = fabs(n[2]);
|
|
|
|
if ( nx <= ny && nx <= nz )
|
|
i1 = 0;
|
|
if ( ny <= nx && ny <= nz )
|
|
i1 = 1;
|
|
if ( nz <= nx && nz <= ny )
|
|
i1 = 2;
|
|
|
|
switch ( i1 )
|
|
{
|
|
case 0:
|
|
if ( ny < nz )
|
|
i2 = 1;
|
|
else
|
|
i2 = 2;
|
|
break;
|
|
case 1:
|
|
if ( nx < nz )
|
|
i2 = 0;
|
|
else
|
|
i2 = 2;
|
|
break;
|
|
case 2:
|
|
if ( nx < ny )
|
|
i2 = 0;
|
|
else
|
|
i2 = 1;
|
|
break;
|
|
}
|
|
|
|
v1.mTexel[0] = tp1[i1]*TSCALE1;
|
|
v1.mTexel[1] = tp1[i2]*TSCALE1;
|
|
|
|
v2.mTexel[0] = tp2[i1]*TSCALE1;
|
|
v2.mTexel[1] = tp2[i2]*TSCALE1;
|
|
|
|
v3.mTexel[0] = tp3[i1]*TSCALE1;
|
|
v3.mTexel[1] = tp3[i2]*TSCALE1;
|
|
|
|
callback->ConvexHullTriangle(v3,v2,v1);
|
|
}
|
|
|
|
//==================================================================================
|
|
double HullLibrary::ComputeNormal(double *n,const double *A,const double *B,const double *C)
|
|
{
|
|
double 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 = sqrt((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;
|
|
}
|
|
|
|
|
|
|
|
};
|