diff --git a/Ice/IceAABB.cpp b/Ice/IceAABB.cpp new file mode 100644 index 0000000..af63598 --- /dev/null +++ b/Ice/IceAABB.cpp @@ -0,0 +1,405 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains AABB-related code. + * \file IceAABB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * AABB class. + * \class AABB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the sum of two AABBs. + * \param aabb [in] the other AABB + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABB& AABB::Add(const AABB& aabb) +{ + // Compute new min & max values + Point Min; GetMin(Min); + Point Tmp; aabb.GetMin(Tmp); + Min.Min(Tmp); + + Point Max; GetMax(Max); + aabb.GetMax(Tmp); + Max.Max(Tmp); + + // Update this + SetMinMax(Min, Max); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a cube from the AABB. + * \param cube [out] the cube AABB + * \return cube edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABB::MakeCube(AABB& cube) const +{ + Point Ext; GetExtents(Ext); + float Max = Ext.Max(); + + Point Cnt; GetCenter(Cnt); + cube.SetCenterExtents(Cnt, Point(Max, Max, Max)); + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a sphere from the AABB. + * \param sphere [out] sphere containing the AABB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABB::MakeSphere(Sphere& sphere) const +{ + GetExtents(sphere.mCenter); + sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f; // To make sure sphere::Contains(*this) succeeds + GetCenter(sphere.mCenter); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a box is inside another box. + * \param box [in] the other AABB + * \return true if current box is inside input box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABB::IsInside(const AABB& box) const +{ + if(box.GetMin(0)>GetMin(0)) return false; + if(box.GetMin(1)>GetMin(1)) return false; + if(box.GetMin(2)>GetMin(2)) return false; + if(box.GetMax(0) max.x) ? 2 : 0) // 2 = right + + ((local_eye.y < min.y) ? 4 : 0) // 4 = bottom + + ((local_eye.y > max.y) ? 8 : 0) // 8 = top + + ((local_eye.z < min.z) ? 16 : 0) // 16 = front + + ((local_eye.z > max.z) ? 32 : 0); // 32 = back + + // Look up number of vertices in outline + num = (sdword)gIndexList[pos][7]; + // Zero indicates invalid case + if(!num) return null; + + return &gIndexList[pos][0]; +} + +// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box + +//const Point& eye, //eye point (in bbox object coordinates) +//const AABB& box, //3d bbox +//const Matrix4x4& mat, //free transformation for bbox +//float width, float height, int& num) +float AABB::ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const +{ + const sbyte* Outline = ComputeOutline(eye, num); + if(!Outline) return -1.0f; + + // Compute box vertices + Point vertexBox[8], dst[8]; + ComputePoints(vertexBox); + + // Transform all outline corners into 2D screen space + for(sdword i=0;i GetMax(0) || p.x < GetMin(0)) return FALSE; \ + if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \ + if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \ + return TRUE; \ + } enum AABBType { - AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered. - AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated. + AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered. + AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated. + AABB_FORCE_DWORD = 0x7fffffff, }; #ifdef USE_MINMAX - class MESHMERIZER_API AABB + struct ICEMATHS_API ShadowAABB + { + Point mMin; + Point mMax; + }; + + class ICEMATHS_API AABB { public: //! Constructor @@ -87,17 +110,16 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Extend(const Point& p) - { - if(p.x > mMax.x) mMax.x = p.x; - if(p.x < mMin.x) mMin.x = p.x; + { + if(p.x > mMax.x) mMax.x = p.x; + if(p.x < mMin.x) mMin.x = p.x; - if(p.y > mMax.y) mMax.y = p.y; - if(p.y < mMin.y) mMin.y = p.y; - - if(p.z > mMax.z) mMax.z = p.z; - if(p.z < mMin.z) mMin.z = p.z; - } + if(p.y > mMax.y) mMax.y = p.y; + if(p.y < mMin.y) mMin.y = p.y; + if(p.z > mMax.z) mMax.z = p.z; + if(p.z < mMin.z) mMin.z = p.z; + } // Data access //! Get min point of the box @@ -136,17 +158,17 @@ * \return true on intersection */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool Intersect(const AABB& a) const - { - if(mMax.x < a.mMin.x - || a.mMax.x < mMin.x - || mMax.y < a.mMin.y - || a.mMax.y < mMin.y - || mMax.z < a.mMin.z - || a.mMax.z < mMin.z) return false; + inline_ BOOL Intersect(const AABB& a) const + { + if(mMax.x < a.mMin.x + || a.mMax.x < mMin.x + || mMax.y < a.mMin.y + || a.mMax.y < mMin.y + || mMax.z < a.mMin.z + || a.mMax.z < mMin.z) return FALSE; - return true; - } + return TRUE; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -156,11 +178,11 @@ * \return true on intersection */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool Intersect(const AABB& a, udword axis) const - { - if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return false; - return true; - } + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE; + return TRUE; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -171,29 +193,29 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const - { - // The three edges transformed: you can efficiently transform an X-only vector - // by just getting the "X" column of the matrix - Point vx,vy,vz; - mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x); - mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y); - mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z); - - // Transform the min point - aabb.mMin = aabb.mMax = mMin * mtx; - - // Take the transformed min & axes and find new extents - // Using CPU code in the right place is faster... - if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x; - if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y; - if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z; - if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x; - if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y; - if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z; - if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x; - if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y; - if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z; - } + { + // The three edges transformed: you can efficiently transform an X-only vector + // by just getting the "X" column of the matrix + Point vx,vy,vz; + mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x); + mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y); + mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z); + + // Transform the min point + aabb.mMin = aabb.mMax = mMin * mtx; + + // Take the transformed min & axes and find new extents + // Using CPU code in the right place is faster... + if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x; + if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y; + if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z; + if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x; + if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y; + if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z; + if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x; + if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y; + if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -201,41 +223,40 @@ * \return true if the box is valid */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool IsValid() const - { - // Consistency condition for (Min, Max) boxes: min < max - if(mMin.x > mMax.x) return false; - if(mMin.y > mMax.y) return false; - if(mMin.z > mMax.z) return false; - return true; - } + inline_ BOOL IsValid() const + { + // Consistency condition for (Min, Max) boxes: min < max + if(mMin.x > mMax.x) return FALSE; + if(mMin.y > mMax.y) return FALSE; + if(mMin.z > mMax.z) return FALSE; + return TRUE; + } //! Operator for AABB *= float. Scales the extents, keeps same center. inline_ AABB& operator*=(float s) - { - Point Center; GetCenter(Center); - Point Extents; GetExtents(Extents); - SetCenterExtents(Center, Extents * s); - return *this; - } + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents * s); + return *this; + } //! Operator for AABB /= float. Scales the extents, keeps same center. inline_ AABB& operator/=(float s) - { - Point Center; GetCenter(Center); - Point Extents; GetExtents(Extents); - SetCenterExtents(Center, Extents / s); - return *this; - } + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents / s); + return *this; + } //! Operator for AABB += Point. Translates the box. inline_ AABB& operator+=(const Point& trans) - { - mMin+=trans; - mMax+=trans; - return *this; - } - + { + mMin+=trans; + mMax+=trans; + return *this; + } private: Point mMin; //!< Min point Point mMax; //!< Max point @@ -243,7 +264,7 @@ #else - class MESHMERIZER_API AABB + class ICEMATHS_API AABB { public: //! Constructor @@ -301,22 +322,21 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Extend(const Point& p) - { - Point Max = mCenter + mExtents; - Point Min = mCenter - mExtents; + { + Point Max = mCenter + mExtents; + Point Min = mCenter - mExtents; - if(p.x > Max.x) Max.x = p.x; - if(p.x < Min.x) Min.x = p.x; + if(p.x > Max.x) Max.x = p.x; + if(p.x < Min.x) Min.x = p.x; - if(p.y > Max.y) Max.y = p.y; - if(p.y < Min.y) Min.y = p.y; + if(p.y > Max.y) Max.y = p.y; + if(p.y < Min.y) Min.y = p.y; - if(p.z > Max.z) Max.z = p.z; - if(p.z < Min.z) Min.z = p.z; - - SetMinMax(Min, Max); - } + if(p.z > Max.z) Max.z = p.z; + if(p.z < Min.z) Min.z = p.z; + SetMinMax(Min, Max); + } // Data access //! Get min point of the box @@ -355,13 +375,13 @@ * \return true on intersection */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool Intersect(const AABB& a) const - { - float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return false; - float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return false; - float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return false; - return true; - } + inline_ BOOL Intersect(const AABB& a) const + { + float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE; + float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE; + float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE; + return TRUE; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -371,12 +391,12 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ bool GomezIntersect(const AABB& a) - { - Point T = mCenter - a.mCenter; // Vector from A to B - return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x)) - && (fabsf(T.y) <= (a.mExtents.y + mExtents.y)) - && (fabsf(T.z) <= (a.mExtents.z + mExtents.z))); - } + { + Point T = mCenter - a.mCenter; // Vector from A to B + return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x)) + && (fabsf(T.y) <= (a.mExtents.y + mExtents.y)) + && (fabsf(T.z) <= (a.mExtents.z + mExtents.z))); + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -386,13 +406,13 @@ * \return true on intersection */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool Intersect(const AABB& a, udword axis) const - { - float t = mCenter[axis] - a.mCenter[axis]; - float e = a.mExtents[axis] + mExtents[axis]; - if(AIR(t) > IR(e)) return false; - return true; - } + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + float t = mCenter[axis] - a.mCenter[axis]; + float e = a.mExtents[axis] + mExtents[axis]; + if(AIR(t) > IR(e)) return FALSE; + return TRUE; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -402,24 +422,24 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const - { - // Compute new center - aabb.mCenter = mCenter * mtx; + { + // Compute new center + aabb.mCenter = mCenter * mtx; - // Compute new extents. FPU code & CPU code have been interleaved for improved performance. - Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x); - IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff; + // Compute new extents. FPU code & CPU code have been interleaved for improved performance. + Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x); + IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff; - Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y); - IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff; + Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y); + IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff; - Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z); - IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff; + Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z); + IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff; - aabb.mExtents.x = Ex.x + Ey.x + Ez.x; - aabb.mExtents.y = Ex.y + Ey.y + Ez.y; - aabb.mExtents.z = Ex.z + Ey.z + Ez.z; - } + aabb.mExtents.x = Ex.x + Ey.x + Ez.x; + aabb.mExtents.y = Ex.y + Ey.y + Ez.y; + aabb.mExtents.z = Ex.z + Ey.z + Ez.z; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -427,14 +447,14 @@ * \return true if the box is valid */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool IsValid() const - { - // Consistency condition for (Center, Extents) boxes: Extents >= 0 - if(IS_NEGATIVE_FLOAT(mExtents.x)) return false; - if(IS_NEGATIVE_FLOAT(mExtents.y)) return false; - if(IS_NEGATIVE_FLOAT(mExtents.z)) return false; - return true; - } + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0 + if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE; + return TRUE; + } //! Operator for AABB *= float. Scales the extents, keeps same center. inline_ AABB& operator*=(float s) { mExtents*=s; return *this; } @@ -444,11 +464,10 @@ //! Operator for AABB += Point. Translates the box. inline_ AABB& operator+=(const Point& trans) - { - mCenter+=trans; - return *this; - } - + { + mCenter+=trans; + return *this; + } private: Point mCenter; //!< AABB Center Point mExtents; //!< x, y and z extents @@ -468,14 +487,15 @@ if(p.z < min.z) min.z = p.z; } - inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nbpts) + inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts) { if(list) { Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); - while(nbpts--) + while(nb_pts--) { +// _prefetch(list+1); // off by one ? ComputeMinMax(*list++, Mini, Maxi); } aabb.SetMinMax(Mini, Maxi); diff --git a/Ice/IceAxes.h b/Ice/IceAxes.h new file mode 100644 index 0000000..39004a9 --- /dev/null +++ b/Ice/IceAxes.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains axes definition. + * \file IceAxes.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEAXES_H__ +#define __ICEAXES_H__ + + enum PointComponent + { + _X = 0, + _Y = 1, + _Z = 2, + _W = 3, + + _FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (_X)|(_Y<<2)|(_Z<<4), + AXES_XZY = (_X)|(_Z<<2)|(_Y<<4), + AXES_YXZ = (_Y)|(_X<<2)|(_Z<<4), + AXES_YZX = (_Y)|(_Z<<2)|(_X<<4), + AXES_ZXY = (_Z)|(_X<<2)|(_Y<<4), + AXES_ZYX = (_Z)|(_Y<<2)|(_X<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Axes + { + public: + + inline_ Axes(AxisOrder order) + { + mAxis0 = (order ) & 3; + mAxis1 = (order>>2) & 3; + mAxis2 = (order>>4) & 3; + } + inline_ ~Axes() {} + + udword mAxis0; + udword mAxis1; + udword mAxis2; + }; + +#endif // __ICEAXES_H__ diff --git a/OpcodeDistrib/OPC_BoundingSphere.h b/Ice/IceBoundingSphere.h similarity index 63% rename from OpcodeDistrib/OPC_BoundingSphere.h rename to Ice/IceBoundingSphere.h index 13b369c..c66524c 100644 --- a/OpcodeDistrib/OPC_BoundingSphere.h +++ b/Ice/IceBoundingSphere.h @@ -12,7 +12,16 @@ #ifndef __ICEBOUNDINGSPHERE_H__ #define __ICEBOUNDINGSPHERE_H__ - class MESHMERIZER_API Sphere + enum BSphereMethod + { + BS_NONE, + BS_GEMS, + BS_MINIBALL, + + BS_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Sphere { public: //! Constructor @@ -20,21 +29,21 @@ //! Constructor inline_ Sphere(const Point& center, float radius) : mCenter(center), mRadius(radius) {} //! Constructor - Sphere(const udword n, Point* p); + Sphere(udword nb_verts, const Point* verts); //! Copy constructor inline_ Sphere(const Sphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius) {} //! Destructor inline_ ~Sphere() {} - bool Compute(udword nbverts, Point* verts); - bool FastCompute(udword nbverts, Point* verts); + BSphereMethod Compute(udword nb_verts, const Point* verts); + bool FastCompute(udword nb_verts, const Point* verts); // Access methods - inline_ const Point& GetCenter() const { return mCenter; } - inline_ float GetRadius() const { return mRadius; } + inline_ const Point& GetCenter() const { return mCenter; } + inline_ float GetRadius() const { return mRadius; } - inline_ const Point& Center() const { return mCenter; } - inline_ float Radius() const { return mRadius; } + inline_ const Point& Center() const { return mCenter; } + inline_ float Radius() const { return mRadius; } inline_ Sphere& Set(const Point& center, float radius) { mCenter = center; mRadius = radius; return *this; } inline_ Sphere& SetCenter(const Point& center) { mCenter = center; return *this; } @@ -47,9 +56,9 @@ * \return true if inside the sphere */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool Contains(const Point& p) const + inline_ bool Contains(const Point& p) const { - return mCenter.SquareDistance(p) < mRadius*mRadius; + return mCenter.SquareDistance(p) <= mRadius*mRadius; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -61,8 +70,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ bool Contains(const Sphere& sphere) const { + // If our radius is the smallest, we can't possibly contain the other sphere + if(mRadius < sphere.mRadius) return false; + // So r is always positive or null now float r = mRadius - sphere.mRadius; - return mCenter.SquareDistance(sphere.mCenter) < r*r; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,9 +89,13 @@ // I assume if all 8 box vertices are inside the sphere, so does the whole box. // Sounds ok but maybe there's a better way? float R2 = mRadius * mRadius; +#ifdef USE_MIN_MAX + const Point& Max = ((ShadowAABB&)&aabb).mMax; + const Point& Min = ((ShadowAABB&)&aabb).mMin; +#else Point Max; aabb.GetMax(Max); Point Min; aabb.GetMin(Min); - +#endif Point p; p.x=Max.x; p.y=Max.y; p.z=Max.z; if(mCenter.SquareDistance(p)>=R2) return FALSE; p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; @@ -93,23 +109,34 @@ return TRUE; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if the sphere intersects another sphere + * \param sphere [in] the other sphere + * \return true if spheres overlap + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Intersect(const Sphere& sphere) const + { + float r = mRadius + sphere.mRadius; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the sphere is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + if(mRadius < 0.0f) return FALSE; + return TRUE; + } public: Point mCenter; //!< Sphere center float mRadius; //!< Sphere radius -#ifdef OLD - private: - Sphere PlanarCircumscribe3 (const Point& p0, const Point& p1, const Point& p2); - Sphere Circumscribe4 (const Point& p0, const Point& p1, const Point& p2, const Point& p3); - Sphere MinFix3 (int n, Point** perm, Point* fixed0, Point* fixed1, Point* fixed2); - Sphere MinFix2 (int n, Point** perm, Point* fixed0, Point* fixed1); - Sphere MinFix1 (int n, Point** perm, Point* fixed0); - - int PointInsideSphere (const Point& p, const Sphere& s); - Sphere MinimalSphere1 (const Point& p); - Sphere MinimalSphere2 (const Point& p0, const Point& p1); - Sphere MinimalSphere3 (const Point& p0, const Point& p1, const Point& p2); - Sphere MinimalSphere4 (const Point& p0, const Point& p1, const Point& p2, const Point& p3); -#endif }; #endif // __ICEBOUNDINGSPHERE_H__ diff --git a/OpcodeDistrib/OPC_Container.cpp b/Ice/IceContainer.cpp similarity index 84% rename from OpcodeDistrib/OPC_Container.cpp rename to Ice/IceContainer.cpp index fb5e0f9..e2c42d1 100644 --- a/OpcodeDistrib/OPC_Container.cpp +++ b/Ice/IceContainer.cpp @@ -59,6 +59,20 @@ Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurN SetSize(size); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Copy constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif + *this = object; +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Destructor. Frees everything and leaves. @@ -73,6 +87,23 @@ Container::~Container() #endif } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container& Container::Empty() +{ +#ifdef CONTAINER_STATS + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + DELETEARRAY(mEntries); + mCurNbEntries = mMaxNbEntries = 0; + return *this; +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Resizes the container. @@ -259,17 +290,17 @@ bool Container::DeleteKeepingOrder(udword entry) /** * Gets the next entry, starting from input one. * \param entry [in/out] On input, the entry to look for. On output, the next entry - * \param wrap [in] true to wrap at the end of the array + * \param find_mode [in] wrap/clamp * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Container& Container::FindNext(udword& entry, bool wrap) +Container& Container::FindNext(udword& entry, FindMode find_mode) { udword Location; if(Contains(entry, &Location)) { Location++; - if(Location==mCurNbEntries) Location = wrap ? 0 : mCurNbEntries-1; + if(Location==mCurNbEntries) Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1; entry = mEntries[Location]; } return *this; @@ -279,17 +310,17 @@ Container& Container::FindNext(udword& entry, bool wrap) /** * Gets the previous entry, starting from input one. * \param entry [in/out] On input, the entry to look for. On output, the previous entry - * \param wrap [in] true to wrap at the end of the array + * \param find_mode [in] wrap/clamp * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Container& Container::FindPrev(udword& entry, bool wrap) +Container& Container::FindPrev(udword& entry, FindMode find_mode) { udword Location; if(Contains(entry, &Location)) { Location--; - if(Location==0xffffffff) Location = wrap ? mCurNbEntries-1 : 0; + if(Location==0xffffffff) Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0; entry = mEntries[Location]; } return *this; @@ -305,3 +336,10 @@ udword Container::GetUsedRam() const { return sizeof(Container) + mMaxNbEntries * sizeof(udword); } + +void Container::operator=(const Container& object) +{ + SetSize(object.GetNbEntries()); + CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(udword)); + mCurNbEntries = mMaxNbEntries; +} diff --git a/OpcodeDistrib/OPC_Container.h b/Ice/IceContainer.h similarity index 72% rename from OpcodeDistrib/OPC_Container.h rename to Ice/IceContainer.h index 2474fda..9f06ada 100644 --- a/OpcodeDistrib/OPC_Container.h +++ b/Ice/IceContainer.h @@ -14,11 +14,20 @@ #define CONTAINER_STATS + enum FindMode + { + FIND_CLAMP, + FIND_WRAP, + + FIND_FORCE_DWORD = 0x7fffffff + }; + class ICECORE_API Container { public: // Constructor / Destructor Container(); + Container(const Container& object); Container(udword size, float growth_factor); ~Container(); // Management @@ -36,25 +45,25 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ Container& Add(udword entry) - { - // Resize if needed - if(mCurNbEntries==mMaxNbEntries) Resize(); + { + // Resize if needed + if(mCurNbEntries==mMaxNbEntries) Resize(); - // Add new entry - mEntries[mCurNbEntries++] = entry; - return *this; - } + // Add new entry + mEntries[mCurNbEntries++] = entry; + return *this; + } inline_ Container& Add(const udword* entries, udword nb) - { - // Resize if needed - if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); + { + // Resize if needed + if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); - // Add new entry - CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); - mCurNbEntries+=nb; - return *this; - } + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); + mCurNbEntries+=nb; + return *this; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -70,32 +79,32 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ Container& Add(float entry) - { - // Resize if needed - if(mCurNbEntries==mMaxNbEntries) Resize(); + { + // Resize if needed + if(mCurNbEntries==mMaxNbEntries) Resize(); - // Add new entry - mEntries[mCurNbEntries++] = IR(entry); - return *this; - } + // Add new entry + mEntries[mCurNbEntries++] = IR(entry); + return *this; + } inline_ Container& Add(const float* entries, udword nb) - { - // Resize if needed - if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); + { + // Resize if needed + if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); - // Add new entry - CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float)); - mCurNbEntries+=nb; - return *this; - } + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float)); + mCurNbEntries+=nb; + return *this; + } //! Add unique [slow] - Container& AddUnique(udword entry) - { - if(!Contains(entry)) Add(entry); - return *this; - } + inline_ Container& AddUnique(udword entry) + { + if(!Contains(entry)) Add(entry); + return *this; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -104,15 +113,7 @@ * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ Container& Empty() - { - #ifdef CONTAINER_STATS - mUsedRam-=mMaxNbEntries*sizeof(udword); - #endif - DELETEARRAY(mEntries); - mCurNbEntries = mMaxNbEntries = 0; - return *this; - } + Container& Empty(); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -122,11 +123,17 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void Reset() - { - // Avoid the write if possible - // ### CMOV - if(mCurNbEntries) mCurNbEntries = 0; - } + { + // Avoid the write if possible + // ### CMOV + if(mCurNbEntries) mCurNbEntries = 0; + } + + // HANDLE WITH CARE + inline_ void ForceSize(udword size) + { + mCurNbEntries = size; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -157,30 +164,32 @@ inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; } // Helpers - Container& FindNext(udword& entry, bool wrap=false); - Container& FindPrev(udword& entry, bool wrap=false); + Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP); + Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP); // Data access. - inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. - inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry - inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. + inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. + inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry + inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. + + inline_ udword GetFirst() const { return mEntries[0]; } + inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; } // Growth control - inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor - inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor + inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor + inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor + inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full + inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty - //! Access as an array - inline_ udword& operator[](udword i) const { ASSERT(i>=0 && i=0 && i=0 && i>1; } - inline_ Pair* GetPairs() const { return (Pair*)GetEntries(); } - - Pairs& AddPair(const Pair& p) { Add(p.id0).Add(p.id1); return *this; } - }; - #endif // __ICECONTAINER_H__ diff --git a/OpcodeDistrib/OPC_FPU.h b/Ice/IceFPU.h similarity index 74% rename from OpcodeDistrib/OPC_FPU.h rename to Ice/IceFPU.h index 967fc41..9e57960 100644 --- a/OpcodeDistrib/OPC_FPU.h +++ b/Ice/IceFPU.h @@ -118,8 +118,21 @@ } //! Is the float valid ? - inline_ bool IsNAN(float value) { return ((*(udword*)&value)&0x7f800000)==0x7f800000; } - #define NaN(value) (!((value>=0) || (value<0))) + inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; } + inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; } + inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; } + inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; } + + inline_ bool IsValidFloat(float value) + { + if(IsNAN(value)) return false; + if(IsIndeterminate(value)) return false; + if(IsPlusInf(value)) return false; + if(IsMinusInf(value)) return false; + return true; + } + + #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x)); /* //! FPU precision setting function. @@ -180,6 +193,57 @@ #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 + #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3 + #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3 + #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3 + #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3 + + #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4 + #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4 + #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4 + #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4 + + #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5 + #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5 + #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5 + #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5 + + #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6 + #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6 + #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6 + #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6 + + #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7 + #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7 + #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7 + #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7 + + //! A global function to find MAX(a,b) using FCOMI/FCMOV + inline_ float FCMax2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + + //! A global function to find MIN(a,b) using FCOMI/FCMOV + inline_ float FCMin2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVNB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + //! A global function to find MAX(a,b,c) using FCOMI/FCMOV inline_ float FCMax3(float a, float b, float c) { @@ -227,6 +291,7 @@ FPU_FLOOR = 0, FPU_CEIL = 1, FPU_BEST = 2, + FPU_FORCE_DWORD = 0x7fffffff }; diff --git a/Ice/IceHPoint.cpp b/Ice/IceHPoint.cpp new file mode 100644 index 0000000..0e730f8 --- /dev/null +++ b/Ice/IceHPoint.cpp @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous points. + * \file IceHPoint.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Homogeneous point. + * + * Use it: + * - for clipping in homogeneous space (standard way) + * - to differentiate between points (w=1) and vectors (w=0). + * - in some cases you can also use it instead of Point for padding reasons. + * + * \class HPoint + * \author Pierre Terdiman + * \version 1.0 + * \warning No cross-product in 4D. + * \warning HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Point Mul = HPoint * Matrix3x3; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point HPoint::operator*(const Matrix3x3& mat) const +{ + return Point( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint Mul = HPoint * Matrix4x4; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint HPoint::operator*(const Matrix4x4& mat) const +{ + return HPoint( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2], + x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint *= Matrix4x4 +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint& HPoint::operator*=(const Matrix4x4& mat) +{ + float xp = x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0]; + float yp = x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1]; + float zp = x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2]; + float wp = x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]; + + x = xp; y = yp; z = zp; w = wp; + + return *this; +} + diff --git a/OpcodeDistrib/OPC_HPoint.h b/Ice/IceHPoint.h similarity index 89% rename from OpcodeDistrib/OPC_HPoint.h rename to Ice/IceHPoint.h index 635e447..3065d8e 100644 --- a/OpcodeDistrib/OPC_HPoint.h +++ b/Ice/IceHPoint.h @@ -21,7 +21,7 @@ //! Constructor from floats inline_ HPoint(float _x, float _y, float _z, float _w=0.0f) : Point(_x, _y, _z), w(_w) {} //! Constructor from array - inline_ HPoint(float f[4]) : Point(f), w(f[3]) {} + inline_ HPoint(const float f[4]) : Point(f), w(f[3]) {} //! Constructor from a Point inline_ HPoint(const Point& p, float _w=0.0f) : Point(p), w(_w) {} //! Destructor @@ -33,19 +33,19 @@ //! Assignment from values inline_ HPoint& Set(float _x, float _y, float _z, float _w ) { x = _x; y = _y; z = _z; w = _w; return *this; } //! Assignment from array - inline_ HPoint& Set(float f[4]) { x = f[_X]; y = f[_Y]; z = f[_Z]; w = f[_W]; return *this; } + inline_ HPoint& Set(const float f[4]) { x = f[_X]; y = f[_Y]; z = f[_Z]; w = f[_W]; return *this; } //! Assignment from another h-point inline_ HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; } //! Add a vector inline_ HPoint& Add(float _x, float _y, float _z, float _w ) { x += _x; y += _y; z += _z; w += _w; return *this; } //! Add a vector - inline_ HPoint& Add(float f[4]) { x += f[_X]; y += f[_Y]; z += f[_Z]; w += f[_W]; return *this; } + inline_ HPoint& Add(const float f[4]) { x += f[_X]; y += f[_Y]; z += f[_Z]; w += f[_W]; return *this; } //! Subtract a vector inline_ HPoint& Sub(float _x, float _y, float _z, float _w ) { x -= _x; y -= _y; z -= _z; w -= _w; return *this; } //! Subtract a vector - inline_ HPoint& Sub(float f[4]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; w -= f[_W]; return *this; } + inline_ HPoint& Sub(const float f[4]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; w -= f[_W]; return *this; } //! Multiplies by a scalar inline_ HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } @@ -66,18 +66,18 @@ //! Normalize the vector inline_ HPoint& Normalize() - { - float M = Magnitude(); - if(M) - { - M = 1.0f / M; - x *= M; - y *= M; - z *= M; - w *= M; - } - return *this; - } + { + float M = Magnitude(); + if(M) + { + M = 1.0f / M; + x *= M; + y *= M; + z *= M; + w *= M; + } + return *this; + } // Arithmetic operators //! Operator for HPoint Negate = - HPoint; diff --git a/Ice/IceIndexedTriangle.cpp b/Ice/IceIndexedTriangle.cpp new file mode 100644 index 0000000..18f4b7b --- /dev/null +++ b/Ice/IceIndexedTriangle.cpp @@ -0,0 +1,548 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an indexed triangle class. + * + * \class Triangle + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Flip() +{ + Swap(mVRef[1], mVRef[2]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \param verts [in] the list of indexed vertices + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Area(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return ((p0-p1)^(p0-p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \param verts [in] the list of indexed vertices + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Perimeter(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \param verts [in] the list of indexed vertices + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Compacity(const Point* verts) const +{ + if(!verts) return 0.0f; + float P = Perimeter(verts); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area(verts)/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Normal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::DenormalizedNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param verts [in] the list of indexed vertices + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Center(const Point* verts, Point& center) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + center = (p0+p1+p2)*INV3; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the centered normal + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::CenteredNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + Point Center = (p0+p1+p2)*INV3; + normal = Center + ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a random point within the triangle. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::RandomPoint(const Point* verts, Point& random) const +{ + if(!verts) return; + + // Random barycentric coords + float Alpha = UnitRandomFloat(); + float Beta = UnitRandomFloat(); + float Gamma = UnitRandomFloat(); + float OneOverTotal = 1.0f / (Alpha + Beta + Gamma); + Alpha *= OneOverTotal; + Beta *= OneOverTotal; + Gamma *= OneOverTotal; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + random = Alpha*p0 + Beta*p1 + Gamma*p2; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsVisible(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling + return (Normal | source) >= 0.0f; + +// Same as: +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source) > PL.d; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::BackfaceCulling(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute base +// Point Base = (p0 + p1 + p2)*INV3; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling +// return (Normal | (source - Base)) >= 0.0f; + return (Normal | (source - p0)) >= 0.0f; + +// Same as: (but a bit faster) +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source)>0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the occlusion potential of the triangle. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which occlusion potential must be computed + * \return the occlusion potential + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::ComputeOcclusionPotential(const Point* verts, const Point& view) const +{ + if(!verts) return 0.0f; + // Occlusion potential: -(A * (N|V) / d^2) + // A = polygon area + // N = polygon normal + // V = view vector + // d = distance viewpoint-center of polygon + + float A = Area(verts); + Point N; Normal(verts, N); + Point C; Center(verts, C); + float d = view.Distance(C); + return -(A*(N|view))/(d*d); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Replaces a vertex reference with another one. + * \param oldref [in] the vertex reference to replace + * \param newref [in] the new vertex reference + * \return true if success, else false if the input vertex reference doesn't belong to the triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::ReplaceVertex(udword oldref, udword newref) +{ + if(mVRef[0]==oldref) { mVRef[0] = newref; return true; } + else if(mVRef[1]==oldref) { mVRef[1] = newref; return true; } + else if(mVRef[2]==oldref) { mVRef[2] = newref; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the triangle is degenerate or not. A degenerate triangle has two common vertex references. This is a zero-area triangle. + * \return true if the triangle is degenerate + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsDegenerate() const +{ + if(mVRef[0]==mVRef[1]) return true; + if(mVRef[1]==mVRef[2]) return true; + if(mVRef[2]==mVRef[0]) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(udword ref) const +{ + if(mVRef[0]==ref) return true; + if(mVRef[1]==ref) return true; + if(mVRef[2]==ref) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \param index [out] the corresponding index in the triangle + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(udword ref, udword* index) const +{ + if(mVRef[0]==ref) { *index = 0; return true; } + if(mVRef[1]==ref) { *index = 1; return true; } + if(mVRef[2]==ref) { *index = 2; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Finds an edge in a tri, given two vertex references. + * \param vref0 [in] the edge's first vertex reference + * \param vref1 [in] the edge's second vertex reference + * \return the edge number between 0 and 2, or 0xff if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ubyte IndexedTriangle::FindEdge(udword vref0, udword vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return 0; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return 0; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return 1; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return 1; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return 2; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return 2; + return 0xff; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the last reference given the first two. + * \param vref0 [in] the first vertex reference + * \param vref1 [in] the second vertex reference + * \return the last reference, or INVALID_ID if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword IndexedTriangle::OppositeVertex(udword vref0, udword vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return mVRef[2]; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return mVRef[2]; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return mVRef[1]; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return mVRef[1]; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return mVRef[0]; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return mVRef[0]; + return INVALID_ID; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the three sorted vertex references according to an edge number. + * edgenb = 0 => edge 0-1, returns references 0, 1, 2 + * edgenb = 1 => edge 0-2, returns references 0, 2, 1 + * edgenb = 2 => edge 1-2, returns references 1, 2, 0 + * + * \param edgenb [in] the edge number, 0, 1 or 2 + * \param vref0 [out] the returned first vertex reference + * \param vref1 [out] the returned second vertex reference + * \param vref2 [out] the returned third vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const +{ + if(edgenb==0) + { + vref0 = mVRef[0]; + vref1 = mVRef[1]; + vref2 = mVRef[2]; + } + else if(edgenb==1) + { + vref0 = mVRef[0]; + vref1 = mVRef[2]; + vref2 = mVRef[1]; + } + else if(edgenb==2) + { + vref0 = mVRef[1]; + vref1 = mVRef[2]; + vref2 = mVRef[0]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \param verts [in] the list of indexed vertices + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MinEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Min = MAX_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \param verts [in] the list of indexed vertices + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MaxEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Max = MIN_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param verts [in] the list of indexed vertices + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::ComputePoint(const Point* verts, float u, float v, Point& pt, udword* nearvtx) const +{ + // Checkings + if(!verts) return; + + // Get face in local or global space + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute point coordinates + pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(p0.SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + p1.SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + p2.SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = mVRef[d.SmallestAxis()]; + } +} + + //************************************** + // Angle between two vectors (in radians) + // we use this formula + // uv = |u||v| cos(u,v) + // u ^ v = w + // |w| = |u||v| |sin(u,v)| + //************************************** + float Angle(const Point& u, const Point& v) + { + float NormU = u.Magnitude(); // |u| + float NormV = v.Magnitude(); // |v| + float Product = NormU*NormV; // |u||v| + if(Product==0.0f) return 0.0f; + float OneOverProduct = 1.0f / Product; + + // Cosinus + float Cosinus = (u|v) * OneOverProduct; + + // Sinus + Point w = u^v; + float NormW = w.Magnitude(); + + float AbsSinus = NormW * OneOverProduct; + + // Remove degeneracy + if(AbsSinus > 1.0f) AbsSinus = 1.0f; + if(AbsSinus < -1.0f) AbsSinus = -1.0f; + + if(Cosinus>=0.0f) return asinf(AbsSinus); + else return (PI-asinf(AbsSinus)); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the angle between two triangles. + * \param tri [in] the other triangle + * \param verts [in] the list of indexed vertices + * \return the angle in radians + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Angle(const IndexedTriangle& tri, const Point* verts) const +{ + // Checkings + if(!verts) return 0.0f; + + // Compute face normals + Point n0, n1; + Normal(verts, n0); + tri.Normal(verts, n1); + + // Compute angle + float dp = n0|n1; + if(dp>1.0f) return 0.0f; + if(dp<-1.0f) return PI; + return acosf(dp); + +// return ::Angle(n0,n1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a triangle is the same as another one. + * \param tri [in] the other triangle + * \return true if same triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::Equal(const IndexedTriangle& tri) const +{ + // Test all vertex references + return (HasVertex(tri.mVRef[0]) && + HasVertex(tri.mVRef[1]) && + HasVertex(tri.mVRef[2])); +} diff --git a/Ice/IceIndexedTriangle.h b/Ice/IceIndexedTriangle.h new file mode 100644 index 0000000..0c497ee --- /dev/null +++ b/Ice/IceIndexedTriangle.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEINDEXEDTRIANGLE_H__ +#define __ICEINDEXEDTRIANGLE_H__ + + // Forward declarations + enum CubeIndex; + + // An indexed triangle class. + class ICEMATHS_API IndexedTriangle + { + public: + //! Constructor + inline_ IndexedTriangle() {} + //! Constructor + inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; } + //! Copy constructor + inline_ IndexedTriangle(const IndexedTriangle& triangle) + { + mVRef[0] = triangle.mVRef[0]; + mVRef[1] = triangle.mVRef[1]; + mVRef[2] = triangle.mVRef[2]; + } + //! Destructor + inline_ ~IndexedTriangle() {} + //! Vertex-references + udword mVRef[3]; + + // Methods + void Flip(); + float Area(const Point* verts) const; + float Perimeter(const Point* verts) const; + float Compacity(const Point* verts) const; + void Normal(const Point* verts, Point& normal) const; + void DenormalizedNormal(const Point* verts, Point& normal) const; + void Center(const Point* verts, Point& center) const; + void CenteredNormal(const Point* verts, Point& normal) const; + void RandomPoint(const Point* verts, Point& random) const; + bool IsVisible(const Point* verts, const Point& source) const; + bool BackfaceCulling(const Point* verts, const Point& source) const; + float ComputeOcclusionPotential(const Point* verts, const Point& view) const; + bool ReplaceVertex(udword oldref, udword newref); + bool IsDegenerate() const; + bool HasVertex(udword ref) const; + bool HasVertex(udword ref, udword* index) const; + ubyte FindEdge(udword vref0, udword vref1) const; + udword OppositeVertex(udword vref0, udword vref1) const; + inline_ udword OppositeVertex(ubyte edgenb) const { return mVRef[2-edgenb]; } + void GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const; + float MinEdgeLength(const Point* verts) const; + float MaxEdgeLength(const Point* verts) const; + void ComputePoint(const Point* verts, float u, float v, Point& pt, udword* nearvtx=null) const; + float Angle(const IndexedTriangle& tri, const Point* verts) const; + inline_ Plane PlaneEquation(const Point* verts) const { return Plane(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); } + bool Equal(const IndexedTriangle& tri) const; + CubeIndex ComputeCubeIndex(const Point* verts) const; + }; + +#endif // __ICEINDEXEDTRIANGLE_H__ diff --git a/Ice/IceLSS.h b/Ice/IceLSS.h new file mode 100644 index 0000000..c16c29f --- /dev/null +++ b/Ice/IceLSS.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for line-swept spheres. + * \file IceLSS.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICELSS_H__ +#define __ICELSS_H__ + + class ICEMATHS_API LSS : public Segment + { + public: + //! Constructor + inline_ LSS() {} + //! Constructor + inline_ LSS(const Segment& seg, float radius) : Segment(seg), mRadius(radius) {} + //! Destructor + inline_ ~LSS() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an OBB surrounding the LSS. + * \param box [out] the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeOBB(OBB& box); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the LSS. + * \param pt [in] the point to test + * \return true if inside the LSS + * \warning point and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Point& pt) const { return SquareDistance(pt) <= mRadius*mRadius; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a sphere is contained within the LSS. + * \param sphere [in] the sphere to test + * \return true if inside the LSS + * \warning sphere and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Sphere& sphere) + { + float d = mRadius - sphere.mRadius; + if(d>=0.0f) return SquareDistance(sphere.mCenter) <= d*d; + else return false; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if an LSS is contained within the LSS. + * \param lss [in] the LSS to test + * \return true if inside the LSS + * \warning both LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const LSS& lss) + { + // We check the LSS contains the two spheres at the start and end of the sweep + return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius)); + } + + float mRadius; //!< Sphere radius + }; + +#endif // __ICELSS_H__ diff --git a/OpcodeDistrib/OPC_Plane.cpp b/Ice/IceMatrix3x3.cpp similarity index 61% rename from OpcodeDistrib/OPC_Plane.cpp rename to Ice/IceMatrix3x3.cpp index c50306d..189d39d 100644 --- a/OpcodeDistrib/OPC_Plane.cpp +++ b/Ice/IceMatrix3x3.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains code for planes. - * \file IcePlane.cpp + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.cpp * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -9,8 +9,23 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Plane class. - * \class Plane + * 3x3 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 first row. + * m21 m22 m23 second row. + * m31 m32 m33 third row. + * Stored in memory as m11 m12 m13 m21... + * + * Multiplication rules: + * + * [x'y'z'] = [xyz][M] + * + * x' = x*m11 + y*m21 + z*m31 + * y' = x*m12 + y*m22 + z*m32 + * z' = x*m13 + y*m23 + z*m33 + * + * \class Matrix3x3 * \author Pierre Terdiman * \version 1.0 */ @@ -18,19 +33,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Precompiled Header -#include "Stdafx.h" +#include "Stdafx.h" using namespace IceMaths; -Plane& Plane::Set(const Point &p0, const Point &p1, const Point &p2) +// Cast operator +Matrix3x3::operator Matrix4x4() const { - Point v0 = p1 - p0; - Point v1 = p2 - p0; - - n = v0 ^ v1; - n.Normalize(); - - d = -(p0 | n); - - return *this; + return Matrix4x4( + m[0][0], m[0][1], m[0][2], 0.0f, + m[1][0], m[1][1], m[1][2], 0.0f, + m[2][0], m[2][1], m[2][2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); } diff --git a/Ice/IceMatrix3x3.h b/Ice/IceMatrix3x3.h new file mode 100644 index 0000000..2266fc5 --- /dev/null +++ b/Ice/IceMatrix3x3.h @@ -0,0 +1,496 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX3X3_H__ +#define __ICEMATRIX3X3_H__ + + // Forward declarations + class Quat; + + #define MATRIX3X3_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix3x3 + { + public: + //! Empty constructor + inline_ Matrix3x3() {} + //! Constructor from 9 values + inline_ Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + //! Copy constructor + inline_ Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); } + //! Destructor + inline_ ~Matrix3x3() {} + + //! Assign values + inline_ void Set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; } + + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; } + + //! Scales from a Point. Each row is multiplied by a component. + inline_ void Scale(const Point& p) + { + m[0][0] *= p.x; m[0][1] *= p.x; m[0][2] *= p.x; + m[1][0] *= p.y; m[1][1] *= p.y; m[1][2] *= p.y; + m[2][0] *= p.z; m[2][1] *= p.z; m[2][2] *= p.z; + } + + //! Scales from floats. Each row is multiplied by a value. + inline_ void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[0][1] *= sx; m[0][2] *= sx; + m[1][0] *= sy; m[1][1] *= sy; m[1][2] *= sy; + m[2][0] *= sz; m[2][1] *= sz; m[2][2] *= sz; + } + + //! Copy from a Matrix3x3 + inline_ void Copy(const Matrix3x3& source) { CopyMemory(m, source.m, 9*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x = m[r][0]; p.y = m[r][1]; p.z = m[r][2]; } + //! Returns a row. + inline_ const Point& GetRow(const udword r) const { return *(const Point*)&m[r][0]; } + //! Returns a row. + inline_ Point& GetRow(const udword r) { return *(Point*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0] = p.x; m[r][1] = p.y; m[r][2] = p.z; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x = m[0][c]; p.y = m[1][c]; p.z = m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c] = p.x; m[1][c] = p.y; m[2][c] = p.z; } + + //! Computes the trace. The trace is the sum of the 3 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<3;j++) + { + for(udword i=0;i<3;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Makes a skew-symmetric matrix (a.k.a. Star(*) Matrix) + //! [ 0.0 -a.z a.y ] + //! [ a.z 0.0 -a.x ] + //! [ -a.y a.x 0.0 ] + //! This is also called a "cross matrix" since for any vectors A and B, + //! A^B = Skew(A) * B = - B * Skew(A); + inline_ void SkewSymmetric(const Point& a) + { + m[0][0] = 0.0f; + m[0][1] = -a.z; + m[0][2] = a.y; + + m[1][0] = a.z; + m[1][1] = 0.0f; + m[1][2] = -a.x; + + m[2][0] = -a.y; + m[2][1] = a.x; + m[2][2] = 0.0f; + } + + //! Negates the matrix + inline_ void Neg() + { + m[0][0] = -m[0][0]; m[0][1] = -m[0][1]; m[0][2] = -m[0][2]; + m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; m[1][2] = -m[1][2]; + m[2][0] = -m[2][0]; m[2][1] = -m[2][1]; m[2][2] = -m[2][2]; + } + + //! Neg from another matrix + inline_ void Neg(const Matrix3x3& mat) + { + m[0][0] = -mat.m[0][0]; m[0][1] = -mat.m[0][1]; m[0][2] = -mat.m[0][2]; + m[1][0] = -mat.m[1][0]; m[1][1] = -mat.m[1][1]; m[1][2] = -mat.m[1][2]; + m[2][0] = -mat.m[2][0]; m[2][1] = -mat.m[2][1]; m[2][2] = -mat.m[2][2]; + } + + //! Add another matrix + inline_ void Add(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + } + + //! Sub another matrix + inline_ void Sub(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, const Matrix3x3& b, float s) + { + m[0][0] = a.m[0][0] + b.m[0][0] * s; + m[0][1] = a.m[0][1] + b.m[0][1] * s; + m[0][2] = a.m[0][2] + b.m[0][2] * s; + + m[1][0] = a.m[1][0] + b.m[1][0] * s; + m[1][1] = a.m[1][1] + b.m[1][1] * s; + m[1][2] = a.m[1][2] + b.m[1][2] * s; + + m[2][0] = a.m[2][0] + b.m[2][0] * s; + m[2][1] = a.m[2][1] + b.m[2][1] * s; + m[2][2] = a.m[2][2] + b.m[2][2] * s; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, float s) + { + m[0][0] += a.m[0][0] * s; m[0][1] += a.m[0][1] * s; m[0][2] += a.m[0][2] * s; + m[1][0] += a.m[1][0] * s; m[1][1] += a.m[1][1] * s; m[1][2] += a.m[1][2] * s; + m[2][0] += a.m[2][0] * s; m[2][1] += a.m[2][1] * s; m[2][2] += a.m[2][2] * s; + } + + //! this = A * s + inline_ void Mult(const Matrix3x3& a, float s) + { + m[0][0] = a.m[0][0] * s; m[0][1] = a.m[0][1] * s; m[0][2] = a.m[0][2] * s; + m[1][0] = a.m[1][0] * s; m[1][1] = a.m[1][1] * s; m[1][2] = a.m[1][2] * s; + m[2][0] = a.m[2][0] * s; m[2][1] = a.m[2][1] * s; m[2][2] = a.m[2][2] * s; + } + + inline_ void Add(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] + b.m[0][0]; m[0][1] = a.m[0][1] + b.m[0][1]; m[0][2] = a.m[0][2] + b.m[0][2]; + m[1][0] = a.m[1][0] + b.m[1][0]; m[1][1] = a.m[1][1] + b.m[1][1]; m[1][2] = a.m[1][2] + b.m[1][2]; + m[2][0] = a.m[2][0] + b.m[2][0]; m[2][1] = a.m[2][1] + b.m[2][1]; m[2][2] = a.m[2][2] + b.m[2][2]; + } + + inline_ void Sub(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] - b.m[0][0]; m[0][1] = a.m[0][1] - b.m[0][1]; m[0][2] = a.m[0][2] - b.m[0][2]; + m[1][0] = a.m[1][0] - b.m[1][0]; m[1][1] = a.m[1][1] - b.m[1][1]; m[1][2] = a.m[1][2] - b.m[1][2]; + m[2][0] = a.m[2][0] - b.m[2][0]; m[2][1] = a.m[2][1] - b.m[2][1]; m[2][2] = a.m[2][2] - b.m[2][2]; + } + + //! this = a * b + inline_ void Mult(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[1][0] + a.m[0][2] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[0][1] * b.m[1][2] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[1][2] * b.m[2][0]; + m[1][1] = a.m[1][0] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[2][1]; + m[1][2] = a.m[1][0] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[2][0] * b.m[0][1] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[2][0] * b.m[0][2] + a.m[2][1] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = transpose(a) * b + inline_ void MultAtB(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[1][0] * b.m[1][0] + a.m[2][0] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[1][0] * b.m[1][1] + a.m[2][0] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[1][0] * b.m[1][2] + a.m[2][0] * b.m[2][2]; + m[1][0] = a.m[0][1] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[2][1] * b.m[2][0]; + m[1][1] = a.m[0][1] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[2][1] * b.m[2][1]; + m[1][2] = a.m[0][1] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[2][1] * b.m[2][2]; + m[2][0] = a.m[0][2] * b.m[0][0] + a.m[1][2] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[0][2] * b.m[0][1] + a.m[1][2] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[0][2] * b.m[0][2] + a.m[1][2] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = a * transpose(b) + inline_ void MultABt(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[0][1] + a.m[0][2] * b.m[0][2]; + m[0][1] = a.m[0][0] * b.m[1][0] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[1][2]; + m[0][2] = a.m[0][0] * b.m[2][0] + a.m[0][1] * b.m[2][1] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[0][1] + a.m[1][2] * b.m[0][2]; + m[1][1] = a.m[1][0] * b.m[1][0] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[1][2]; + m[1][2] = a.m[1][0] * b.m[2][0] + a.m[1][1] * b.m[2][1] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[0][1] + a.m[2][2] * b.m[0][2]; + m[2][1] = a.m[2][0] * b.m[1][0] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[1][2]; + m[2][2] = a.m[2][0] * b.m[2][0] + a.m[2][1] * b.m[2][1] + a.m[2][2] * b.m[2][2]; + } + + //! Makes a rotation matrix mapping vector "from" to vector "to". + Matrix3x3& FromTo(const Point& from, const Point& to); + + //! Set a rotation matrix around the X axis. + //! 1 0 0 + //! RX = 0 cx sx + //! 0 -sx cx + void RotX(float angle); + //! Set a rotation matrix around the Y axis. + //! cy 0 -sy + //! RY = 0 1 0 + //! sy 0 cy + void RotY(float angle); + //! Set a rotation matrix around the Z axis. + //! cz sz 0 + //! RZ = -sz cz 0 + //! 0 0 1 + void RotZ(float angle); + //! cy sx.sy -sy.cx + //! RY.RX 0 cx sx + //! sy -sx.cy cx.cy + void RotYX(float y, float x); + + //! Make a rotation matrix about an arbitrary axis + Matrix3x3& Rot(float angle, const Point& axis); + + //! Transpose the matrix. + void Transpose() + { + IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]); + IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]); + IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); + } + + //! this = Transpose(a) + void Transpose(const Matrix3x3& a) + { + m[0][0] = a.m[0][0]; m[0][1] = a.m[1][0]; m[0][2] = a.m[2][0]; + m[1][0] = a.m[0][1]; m[1][1] = a.m[1][1]; m[1][2] = a.m[2][1]; + m[2][0] = a.m[0][2]; m[2][1] = a.m[1][2]; m[2][2] = a.m[2][2]; + } + + //! Compute the determinant of the matrix. We use the rule of Sarrus. + float Determinant() const + { + return (m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1]) + - (m[2][0]*m[1][1]*m[0][2] + m[2][1]*m[1][2]*m[0][0] + m[2][2]*m[1][0]*m[0][1]); + } +/* + //! Compute a cofactor. Used for matrix inversion. + float CoFactor(ubyte row, ubyte column) const + { + static sdword gIndex[3+2] = { 0, 1, 2, 0, 1 }; + return (m[gIndex[row+1]][gIndex[column+1]]*m[gIndex[row+2]][gIndex[column+2]] - m[gIndex[row+2]][gIndex[column+1]]*m[gIndex[row+1]][gIndex[column+2]]); + } +*/ + //! Invert the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix3x3& Invert() + { + float Det = Determinant(); // Must be !=0 + float OneOverDet = 1.0f / Det; + + Matrix3x3 Temp; + Temp.m[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDet; + Temp.m[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDet; + Temp.m[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDet; + Temp.m[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDet; + Temp.m[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDet; + Temp.m[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDet; + Temp.m[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDet; + Temp.m[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDet; + Temp.m[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDet; + + *this = Temp; + + return *this; + } + + Matrix3x3& Normalize(); + + //! this = exp(a) + Matrix3x3& Exp(const Matrix3x3& a); + +void FromQuat(const Quat &q); +void FromQuatL2(const Quat &q, float l2); + + // Arithmetic operators + //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3; + inline_ Matrix3x3 operator+(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2], + m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2], + m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]); + } + + //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3; + inline_ Matrix3x3 operator-(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2], + m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2], + m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]); + } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3; + inline_ Matrix3x3 operator*(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]); + } + + //! Operator for Point Mul = Matrix3x3 * Point; + inline_ Point operator*(const Point& v) const { return Point(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v); } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * float; + inline_ Matrix3x3 operator*(float s) const + { + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Mul = float * Matrix3x3; + inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]); + } + + //! Operator for Matrix3x3 Div = Matrix3x3 / float; + inline_ Matrix3x3 operator/(float s) const + { + if (s) s = 1.0f / s; + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Div = float / Matrix3x3; + inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]); + } + + //! Operator for Matrix3x3 += Matrix3x3 + inline_ Matrix3x3& operator+=(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 -= Matrix3x3 + inline_ Matrix3x3& operator-=(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= Matrix3x3 + inline_ Matrix3x3& operator*=(const Matrix3x3& mat) + { + Point TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= float + inline_ Matrix3x3& operator*=(float s) + { + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + //! Operator for Matrix3x3 /= float + inline_ Matrix3x3& operator/=(float s) + { + if (s) s = 1.0f / s; + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + // Cast operators + //! Cast a Matrix3x3 to a Matrix4x4. + operator Matrix4x4() const; + //! Cast a Matrix3x3 to a Quat. + operator Quat() const; + + inline_ const Point& operator[](int row) const { return *(const Point*)&m[row][0]; } + inline_ Point& operator[](int row) { return *(Point*)&m[row][0]; } + + public: + + float m[3][3]; + }; + +#endif // __ICEMATRIX3X3_H__ + diff --git a/Ice/IceMatrix4x4.cpp b/Ice/IceMatrix4x4.cpp new file mode 100644 index 0000000..4c539c9 --- /dev/null +++ b/Ice/IceMatrix4x4.cpp @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 4x4 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 m14 first row. + * m21 m22 m23 m24 second row. + * m31 m32 m33 m34 third row. + * m41 m42 m43 m44 fourth row. + * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1). + * Stored in memory as m11 m12 m13 m14 m21... + * + * Multiplication rules: + * + * [x'y'z'1] = [xyz1][M] + * + * x' = x*m11 + y*m21 + z*m31 + m41 + * y' = x*m12 + y*m22 + z*m32 + m42 + * z' = x*m13 + y*m23 + z*m33 + m43 + * 1' = 0 + 0 + 0 + m44 + * + * \class Matrix4x4 + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Inverts a PR matrix. (which only contains a rotation and a translation) + * This is faster and less subject to FPU errors than the generic inversion code. + * + * \relates Matrix4x4 + * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) + * \param dest [out] destination matrix + * \param src [in] source matrix + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) +{ + dest.m[0][0] = src.m[0][0]; + dest.m[1][0] = src.m[0][1]; + dest.m[2][0] = src.m[0][2]; + dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]); + + dest.m[0][1] = src.m[1][0]; + dest.m[1][1] = src.m[1][1]; + dest.m[2][1] = src.m[1][2]; + dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]); + + dest.m[0][2] = src.m[2][0]; + dest.m[1][2] = src.m[2][1]; + dest.m[2][2] = src.m[2][2]; + dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]); + + dest.m[0][3] = 0.0f; + dest.m[1][3] = 0.0f; + dest.m[2][3] = 0.0f; + dest.m[3][3] = 1.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the cofactor of the Matrix at a specified location +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::CoFactor(udword row, udword col) const +{ + return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] + + m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] + + m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3]) + - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] + + m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] + + m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the determinant of the Matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::Determinant() const +{ + return m[0][0] * CoFactor(0, 0) + + m[0][1] * CoFactor(0, 1) + + m[0][2] * CoFactor(0, 2) + + m[0][3] * CoFactor(0, 3); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the inverse of the matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Matrix4x4& Matrix4x4::Invert() +{ + float Det = Determinant(); + Matrix4x4 Temp; + + if(fabsf(Det) < MATRIX4X4_EPSILON) + return *this; // The matrix is not invertible! Singular case! + + float IDet = 1.0f / Det; + + Temp.m[0][0] = CoFactor(0,0) * IDet; + Temp.m[1][0] = CoFactor(0,1) * IDet; + Temp.m[2][0] = CoFactor(0,2) * IDet; + Temp.m[3][0] = CoFactor(0,3) * IDet; + Temp.m[0][1] = CoFactor(1,0) * IDet; + Temp.m[1][1] = CoFactor(1,1) * IDet; + Temp.m[2][1] = CoFactor(1,2) * IDet; + Temp.m[3][1] = CoFactor(1,3) * IDet; + Temp.m[0][2] = CoFactor(2,0) * IDet; + Temp.m[1][2] = CoFactor(2,1) * IDet; + Temp.m[2][2] = CoFactor(2,2) * IDet; + Temp.m[3][2] = CoFactor(2,3) * IDet; + Temp.m[0][3] = CoFactor(3,0) * IDet; + Temp.m[1][3] = CoFactor(3,1) * IDet; + Temp.m[2][3] = CoFactor(3,2) * IDet; + Temp.m[3][3] = CoFactor(3,3) * IDet; + + *this = Temp; + + return *this; +} + diff --git a/Ice/IceMatrix4x4.h b/Ice/IceMatrix4x4.h new file mode 100644 index 0000000..d61a461 --- /dev/null +++ b/Ice/IceMatrix4x4.h @@ -0,0 +1,455 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX4X4_H__ +#define __ICEMATRIX4X4_H__ + + // Forward declarations + class PRS; + class PR; + + #define MATRIX4X4_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix4x4 + { +// void LUBackwardSubstitution( sdword *indx, float* b ); +// void LUDecomposition( sdword* indx, float* d ); + + public: + //! Empty constructor. + inline_ Matrix4x4() {} + //! Constructor from 16 values + inline_ Matrix4x4( float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + } + //! Copy constructor + inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } + //! Destructor. + inline_ ~Matrix4x4() {} + + //! Assign values (rotation only) + inline_ Matrix4x4& Set( float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + return *this; + } + //! Assign values + inline_ Matrix4x4& Set( float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + return *this; + } + + //! Copy from a Matrix4x4 + inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; } + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; } + //! Returns a row. + inline_ const HPoint& GetRow(const udword r) const { return *(const HPoint*)&m[r][0]; } + //! Returns a row. + inline_ HPoint& GetRow(const udword r) { return *(HPoint*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; } + //! Returns a column. + inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; } + + // Translation + //! Returns the translation part of the matrix. + inline_ const HPoint& GetTrans() const { return GetRow(3); } + //! Gets the translation part of the matrix + inline_ void GetTrans(Point& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; } + //! Sets the translation part of the matrix, from a Point. + inline_ void SetTrans(const Point& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; } + //! Sets the translation part of the matrix, from a HPoint. + inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; } + //! Sets the translation part of the matrix, from floats. + inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; } + + // Scale + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; } + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; } + //! Scales from a Point. Each row is multiplied by a component. + void Scale(const Point& p) + { + m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z; + m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z; + m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z; + } + //! Scales from floats. Each row is multiplied by a value. + void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz; + m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz; + m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz; + } +/* + //! Returns a row. + inline_ HPoint GetRow(const udword row) const { return mRow[row]; } + //! Sets a row. + inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; } + //! Sets a row. + Matrix4x4& SetRow(const udword row, const Point& p) + { + m[row][0] = p.x; + m[row][1] = p.y; + m[row][2] = p.z; + m[row][3] = (row != 3) ? 0.0f : 1.0f; + return *this; + } + //! Returns a column. + HPoint GetCol(const udword col) const + { + HPoint Res; + Res.x = m[0][col]; + Res.y = m[1][col]; + Res.z = m[2][col]; + Res.w = m[3][col]; + return Res; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const HPoint& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = p.w; + return *this; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const Point& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = (col != 3) ? 0.0f : 1.0f; + return *this; + } +*/ + //! Computes the trace. The trace is the sum of the 4 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; } + //! Computes the trace of the upper 3x3 matrix. + inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + if(IR(m[0][3])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + if(IR(m[1][3])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + if(IR(m[2][3])!=0) return false; + + if(IR(m[3][0])!=0) return false; + if(IR(m[3][1])!=0) return false; + if(IR(m[3][2])!=0) return false; + if(IR(m[3][3])!=IEEE_1_0) return false; + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<4;j++) + { + for(udword i=0;i<4;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Sets a rotation matrix around the X axis. + void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; } + //! Sets a rotation matrix around the Y axis. + void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; } + //! Sets a rotation matrix around the Z axis. + void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; } + + //! Makes a rotation matrix about an arbitrary axis + Matrix4x4& Rot(float angle, Point& p1, Point& p2); + + //! Transposes the matrix. + void Transpose() + { + IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]); + IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]); + IR(m[3][0]) ^= IR(m[0][3]); IR(m[0][3]) ^= IR(m[3][0]); IR(m[3][0]) ^= IR(m[0][3]); + IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); + IR(m[1][3]) ^= IR(m[3][1]); IR(m[3][1]) ^= IR(m[1][3]); IR(m[1][3]) ^= IR(m[3][1]); + IR(m[2][3]) ^= IR(m[3][2]); IR(m[3][2]) ^= IR(m[2][3]); IR(m[2][3]) ^= IR(m[3][2]); + } + + //! Computes a cofactor. Used for matrix inversion. + float CoFactor(udword row, udword col) const; + //! Computes the determinant of the matrix. + float Determinant() const; + //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix4x4& Invert(); +// Matrix& ComputeAxisMatrix(Point& axis, float angle); + + // Cast operators + //! Casts a Matrix4x4 to a Matrix3x3. + inline_ operator Matrix3x3() const + { + return Matrix3x3( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + //! Casts a Matrix4x4 to a Quat. + operator Quat() const; + //! Casts a Matrix4x4 to a PR. + operator PR() const; + + // Arithmetic operators + //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4; + inline_ Matrix4x4 operator+(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]+mat.m[0][0], m[0][1]+mat.m[0][1], m[0][2]+mat.m[0][2], m[0][3]+mat.m[0][3], + m[1][0]+mat.m[1][0], m[1][1]+mat.m[1][1], m[1][2]+mat.m[1][2], m[1][3]+mat.m[1][3], + m[2][0]+mat.m[2][0], m[2][1]+mat.m[2][1], m[2][2]+mat.m[2][2], m[2][3]+mat.m[2][3], + m[3][0]+mat.m[3][0], m[3][1]+mat.m[3][1], m[3][2]+mat.m[3][2], m[3][3]+mat.m[3][3]); + } + + //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4; + inline_ Matrix4x4 operator-(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]-mat.m[0][0], m[0][1]-mat.m[0][1], m[0][2]-mat.m[0][2], m[0][3]-mat.m[0][3], + m[1][0]-mat.m[1][0], m[1][1]-mat.m[1][1], m[1][2]-mat.m[1][2], m[1][3]-mat.m[1][3], + m[2][0]-mat.m[2][0], m[2][1]-mat.m[2][1], m[2][2]-mat.m[2][2], m[2][3]-mat.m[2][3], + m[3][0]-mat.m[3][0], m[3][1]-mat.m[3][1], m[3][2]-mat.m[3][2], m[3][3]-mat.m[3][3]); + } + + //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4; + inline_ Matrix4x4 operator*(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2], + m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2], + m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2], + m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3], + + m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0], + m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1], + m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2], + m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]); + } + + //! Operator for HPoint Mul = Matrix4x4 * HPoint; + inline_ HPoint operator*(const HPoint& v) const { return HPoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v, GetRow(3)|v); } + + //! Operator for Point Mul = Matrix4x4 * Point; + inline_ Point operator*(const Point& v) const + { + return Point( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3], + m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3], + m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] ); + } + + //! Operator for Matrix4x4 Scale = Matrix4x4 * float; + inline_ Matrix4x4 operator*(float s) const + { + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Scale = float * Matrix4x4; + inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3], + s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]); + } + + //! Operator for Matrix4x4 Div = Matrix4x4 / float; + inline_ Matrix4x4 operator/(float s) const + { + if(s) s = 1.0f / s; + + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Div = float / Matrix4x4; + inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3], + s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]); + } + + //! Operator for Matrix4x4 += Matrix4x4; + inline_ Matrix4x4& operator+=(const Matrix4x4& mat) + { + m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3]; + m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3]; + m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3]; + m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 -= Matrix4x4; + inline_ Matrix4x4& operator-=(const Matrix4x4& mat) + { + m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3]; + m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3]; + m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3]; + m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 *= Matrix4x4; + Matrix4x4& operator*=(const Matrix4x4& mat) + { + HPoint TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(3, TempRow); + m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + return *this; + } + + //! Operator for Matrix4x4 *= float; + inline_ Matrix4x4& operator*=(float s) + { + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + //! Operator for Matrix4x4 /= float; + inline_ Matrix4x4& operator/=(float s) + { + if(s) s = 1.0f / s; + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + inline_ const HPoint& operator[](int row) const { return *(const HPoint*)&m[row][0]; } + inline_ HPoint& operator[](int row) { return *(HPoint*)&m[row][0]; } + + public: + + float m[4][4]; + }; + + //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix + inline_ void TransformPoint4x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix + inline_ void TransformPoint3x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); + +#endif // __ICEMATRIX4X4_H__ + diff --git a/OpcodeDistrib/OPC_MemoryMacros.h b/Ice/IceMemoryMacros.h similarity index 95% rename from OpcodeDistrib/OPC_MemoryMacros.h rename to Ice/IceMemoryMacros.h index 33748c6..490ecd1 100644 --- a/OpcodeDistrib/OPC_MemoryMacros.h +++ b/Ice/IceMemoryMacros.h @@ -94,7 +94,7 @@ #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release #ifdef __ICEERROR_H__ - #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUTOFMEMORY); //!< Standard alloc checking. HANDLE WITH CARE. + #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE. #else #define CHECKALLOC(x) if(!x) return false; #endif diff --git a/Ice/IceOBB.cpp b/Ice/IceOBB.cpp new file mode 100644 index 0000000..98dbb71 --- /dev/null +++ b/Ice/IceOBB.cpp @@ -0,0 +1,323 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. + * \file IceOBB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An Oriented Bounding Box (OBB). + * \class OBB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ContainsPoint(const Point& p) const +{ + // Point in OBB test using lazy evaluation and early exits + + // Translate to box space + Point RelPoint = p - mCenter; + + // Point * mRot maps from box space to world space + // mRot * Point maps from world space to box space (what we need here) + + float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z; + if(f >= mExtents.x || f <= -mExtents.x) return false; + + f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z; + if(f >= mExtents.y || f <= -mExtents.y) return false; + + f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z; + if(f >= mExtents.z || f <= -mExtents.z) return false; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::Create(const AABB& aabb, const Matrix4x4& mat) +{ + // Note: must be coherent with Rotate() + + aabb.GetCenter(mCenter); + aabb.GetExtents(mExtents); + // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity). + + // So following what's done in Rotate: + // - x-form the center + mCenter *= mat; + // - combine rotation with identity, i.e. just use given matrix + mRot = mat; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePlanes(Plane* planes) const +{ + // Checkings + if(!planes) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + // Writes normals + planes[0].n = Axis0; + planes[1].n = -Axis0; + planes[2].n = Axis1; + planes[3].n = -Axis1; + planes[4].n = Axis2; + planes[5].n = -Axis2; + + // Compute a point on each plane + Point p0 = mCenter + Axis0 * mExtents.x; + Point p1 = mCenter - Axis0 * mExtents.x; + Point p2 = mCenter + Axis1 * mExtents.y; + Point p3 = mCenter - Axis1 * mExtents.y; + Point p4 = mCenter + Axis2 * mExtents.z; + Point p5 = mCenter - Axis2 * mExtents.z; + + // Compute d + planes[0].d = -(planes[0].n|p0); + planes[1].d = -(planes[1].n|p1); + planes[2].d = -(planes[2].n|p2); + planes[3].d = -(planes[3].n|p3); + planes[4].d = -(planes[4].n|p4); + planes[5].d = -(planes[5].n|p5); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePoints(Point* pts) const +{ + // Checkings + if(!pts) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + Axis0 *= mExtents.x; + Axis1 *= mExtents.y; + Axis2 *= mExtents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + pts[0] = mCenter - Axis0 - Axis1 - Axis2; + pts[1] = mCenter + Axis0 - Axis1 - Axis2; + pts[2] = mCenter + Axis0 + Axis1 - Axis2; + pts[3] = mCenter - Axis0 + Axis1 - Axis2; + pts[4] = mCenter - Axis0 - Axis1 + Axis2; + pts[5] = mCenter + Axis0 - Axis1 + Axis2; + pts[6] = mCenter + Axis0 + Axis1 + Axis2; + pts[7] = mCenter - Axis0 + Axis1 + Axis2; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputeVertexNormals(Point* pts) const +{ + static float VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + if(!pts) return false; + + const Point* VN = (const Point*)VertexNormals; + for(udword i=0;i<8;i++) + { + pts[i] = VN[i] * mRot; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const udword* OBB::GetEdges() const +{ + static udword Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns local edge normals. + * \return edge normals in local space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const Point* OBB::GetLocalEdgeNormals() const +{ + static float EdgeNormals[] = + { + 0, -INVSQRT2, -INVSQRT2, // 0-1 + INVSQRT2, 0, -INVSQRT2, // 1-2 + 0, INVSQRT2, -INVSQRT2, // 2-3 + -INVSQRT2, 0, -INVSQRT2, // 3-0 + + 0, INVSQRT2, INVSQRT2, // 7-6 + INVSQRT2, 0, INVSQRT2, // 6-5 + 0, -INVSQRT2, INVSQRT2, // 5-4 + -INVSQRT2, 0, INVSQRT2, // 4-7 + + INVSQRT2, -INVSQRT2, 0, // 1-5 + INVSQRT2, INVSQRT2, 0, // 6-2 + -INVSQRT2, INVSQRT2, 0, // 3-7 + -INVSQRT2, -INVSQRT2, 0 // 4-0 + }; + return (const Point*)EdgeNormals; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const +{ + ASSERT(edge_index<12); + world_normal = GetLocalEdgeNormals()[edge_index] * mRot; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeLSS(LSS& lss) const +{ + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + switch(mExtents.LargestAxis()) + { + case 0: + lss.mRadius = (mExtents.y + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius); + lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius); + break; + case 1: + lss.mRadius = (mExtents.x + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius); + lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius); + break; + case 2: + lss.mRadius = (mExtents.x + mExtents.y)*0.5f; + lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius); + lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL OBB::IsInside(const OBB& box) const +{ + // Make a 4x4 from the box & inverse it + Matrix4x4 M0Inv; + { + Matrix4x4 M0 = box.mRot; + M0.SetTrans(box.mCenter); + InvertPRMatrix(M0Inv, M0); + } + + // With our inversed 4x4, create box1 in space of box0 + OBB _1in0; + Rotate(M0Inv, _1in0); + + // This should cancel out box0's rotation, i.e. it's now an AABB. + // => Center(0,0,0), Rot(identity) + + // The two boxes are in the same space so now we can compare them. + + // Create the AABB of (box1 in space of box0) + const Matrix3x3& mtx = _1in0.mRot; + + float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x; + if(f > _1in0.mCenter.x) return FALSE; + if(-f < _1in0.mCenter.x) return FALSE; + + f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y; + if(f > _1in0.mCenter.y) return FALSE; + if(-f < _1in0.mCenter.y) return FALSE; + + f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z; + if(f > _1in0.mCenter.z) return FALSE; + if(-f < _1in0.mCenter.z) return FALSE; + + return TRUE; +} diff --git a/Ice/IceOBB.h b/Ice/IceOBB.h new file mode 100644 index 0000000..4747f7f --- /dev/null +++ b/Ice/IceOBB.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. (oriented bounding box) + * \file IceOBB.h + * \author Pierre Terdiman + * \date January, 13, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEOBB_H__ +#define __ICEOBB_H__ + + // Forward declarations + class LSS; + + class ICEMATHS_API OBB + { + public: + //! Constructor + inline_ OBB() {} + //! Constructor + inline_ OBB(const Point& center, const Point& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot) {} + //! Destructor + inline_ ~OBB() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty OBB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() + { + mCenter.Zero(); + mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + mRot.Identity(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ContainsPoint(const Point& p) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Create(const AABB& aabb, const Matrix4x4& mat); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the OBB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param obb [out] the transformed OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const + { + // The extents remain constant + obb.mExtents = mExtents; + // The center gets x-formed + obb.mCenter = mCenter * mtx; + // Combine rotations + obb.mRot = mRot * Matrix3x3(mtx); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(mExtents.x < 0.0f) return FALSE; + if(mExtents.y < 0.0f) return FALSE; + if(mExtents.z < 0.0f) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePlanes(Plane* planes) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePoints(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputeVertexNormals(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const udword* GetEdges() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns local edge normals. + * \return edge normals in local space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const Point* GetLocalEdgeNormals() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeLSS(LSS& lss) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + BOOL IsInside(const OBB& box) const; + + inline_ const Point& GetCenter() const { return mCenter; } + inline_ const Point& GetExtents() const { return mExtents; } + inline_ const Matrix3x3& GetRot() const { return mRot; } + + inline_ void GetRotatedExtents(Matrix3x3& extents) const + { + extents = mRot; + extents.Scale(mExtents); + } + + Point mCenter; //!< B for Box + Point mExtents; //!< B for Bounding + Matrix3x3 mRot; //!< O for Oriented + + // Orientation is stored in row-major format, + // i.e. rows = eigen vectors of the covariance matrix + }; + +#endif // __ICEOBB_H__ diff --git a/Ice/IcePairs.h b/Ice/IcePairs.h new file mode 100644 index 0000000..35e3a07 --- /dev/null +++ b/Ice/IcePairs.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple pair class. + * \file IcePairs.h + * \author Pierre Terdiman + * \date January, 13, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPAIRS_H__ +#define __ICEPAIRS_H__ + + //! A generic couple structure + struct ICECORE_API Pair + { + inline_ Pair() {} + inline_ Pair(udword i0, udword i1) : id0(i0), id1(i1) {} + + udword id0; //!< First index of the pair + udword id1; //!< Second index of the pair + }; + + class ICECORE_API Pairs : private Container + { + public: + // Constructor / Destructor + Pairs() {} + ~Pairs() {} + + inline_ udword GetNbPairs() const { return GetNbEntries()>>1; } + inline_ const Pair* GetPairs() const { return (const Pair*)GetEntries(); } + inline_ const Pair* GetPair(udword i) const { return (const Pair*)&GetEntries()[i+i]; } + + inline_ BOOL HasPairs() const { return IsNotEmpty(); } + + inline_ void ResetPairs() { Reset(); } + inline_ void DeleteLastPair() { DeleteLastEntry(); DeleteLastEntry(); } + + inline_ void AddPair(const Pair& p) { Add(p.id0).Add(p.id1); } + inline_ void AddPair(udword id0, udword id1) { Add(id0).Add(id1); } + }; + +#endif // __ICEPAIRS_H__ diff --git a/OpcodeDistrib/OPC_Ray.cpp b/Ice/IcePlane.cpp similarity index 73% rename from OpcodeDistrib/OPC_Ray.cpp rename to Ice/IcePlane.cpp index 3919b36..36fe473 100644 --- a/OpcodeDistrib/OPC_Ray.cpp +++ b/Ice/IcePlane.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains code for rays. - * \file IceRay.cpp + * Contains code for planes. + * \file IcePlane.cpp * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -9,29 +9,37 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Ray class. - * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity - * \class Ray + * Plane class. + * \class Plane * \author Pierre Terdiman * \version 1.0 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Segment class. - * A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1 - * Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1. - * Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1. - * - * \class Segment - * \author Pierre Terdiman - * \version 1.0 + * Computes the plane equation from 3 points. + * \param p0 [in] first point + * \param p1 [in] second point + * \param p2 [in] third point + * \return Self-reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Plane& Plane::Set(const Point& p0, const Point& p1, const Point& p2) +{ + Point Edge0 = p1 - p0; + Point Edge1 = p2 - p0; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" + n = Edge0 ^ Edge1; + n.Normalize(); -using namespace IceMaths; + d = -(p0 | n); + + return *this; +} diff --git a/OpcodeDistrib/OPC_Plane.h b/Ice/IcePlane.h similarity index 67% rename from OpcodeDistrib/OPC_Plane.h rename to Ice/IcePlane.h index 29d1064..0e0604c 100644 --- a/OpcodeDistrib/OPC_Plane.h +++ b/Ice/IcePlane.h @@ -18,45 +18,44 @@ { public: //! Constructor - inline_ Plane() { } - //! Constructor - inline_ Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); } - //! Constructor - inline_ Plane(const Point& p, const Point& n) { Set(p, n); } - //! Constructor - inline_ Plane(const Point& p0, const Point& p1, const Point& p2) { Set(p0, p1, p2); } - //! Constructor - inline_ Plane(const Point& n, float d) { this->n = n; this->d = d; } + inline_ Plane() { } + //! Constructor from a normal and a distance + inline_ Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); } + //! Constructor from a point on the plane and a normal + inline_ Plane(const Point& p, const Point& n) { Set(p, n); } + //! Constructor from three points + inline_ Plane(const Point& p0, const Point& p1, const Point& p2) { Set(p0, p1, p2); } + //! Constructor from a normal and a distance + inline_ Plane(const Point& _n, float _d) { n = _n; d = _d; } //! Copy constructor - inline_ Plane(const Plane& plane) : n(plane.n), d(plane.d) { } + inline_ Plane(const Plane& plane) : n(plane.n), d(plane.d) { } //! Destructor - inline_ ~Plane() { } + inline_ ~Plane() { } - inline_ Plane& Zero() { n.Zero(); d = 0.0f; return *this; } - inline_ Plane& Set(float nx, float ny, float nz, float d) { n.Set(nx, ny, nz); this->d = d; return *this; } - inline_ Plane& Set(const Point& p, const Point& n) { this->n = n; d = - p | n; return *this; } + inline_ Plane& Zero() { n.Zero(); d = 0.0f; return *this; } + inline_ Plane& Set(float nx, float ny, float nz, float _d) { n.Set(nx, ny, nz); d = _d; return *this; } + inline_ Plane& Set(const Point& p, const Point& _n) { n = _n; d = - p | _n; return *this; } Plane& Set(const Point& p0, const Point& p1, const Point& p2); - inline_ float Distance(const Point& p) const { return (p | n) + d; } - inline_ bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; } + inline_ float Distance(const Point& p) const { return (p | n) + d; } + inline_ bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; } inline_ void Normalize() - { - float Denom = 1.0f / n.Magnitude(); - n.x *= Denom; - n.y *= Denom; - n.z *= Denom; - d *= Denom; - } - + { + float Denom = 1.0f / n.Magnitude(); + n.x *= Denom; + n.y *= Denom; + n.z *= Denom; + d *= Denom; + } public: // Members Point n; //!< The normal to the plane float d; //!< The distance from the origin // Cast operators - inline_ operator Point() const { return n; } -/* inline_ operator HPoint() const { return HPoint(n, d); } + inline_ operator Point() const { return n; } + inline_ operator HPoint() const { return HPoint(n, d); } // Arithmetic operators inline_ Plane operator*(const Matrix4x4& m) const @@ -74,7 +73,6 @@ n = n2; return *this; } -*/ }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -83,20 +81,16 @@ * \param transformed [out] transformed plane * \param plane [in] source plane * \param transform [in] transform matrix + * \warning the plane normal must be unit-length */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void TransformPlane(Plane& transformed, const Plane& plane, const Matrix4x4& transform) { - // Catch the rotation part of the 4x4 matrix - Matrix3x3 Rot = transform; - - // Rotate the normal - transformed.n = plane.n * Rot; + // Rotate the normal using the rotation part of the 4x4 matrix + transformed.n = plane.n * Matrix3x3(transform); // Compute new d - Point Trans; - transform.GetTrans(Trans); - transformed.d = (plane.d * transformed.n - Trans)|transformed.n; + transformed.d = plane.d - (Point(transform.GetTrans())|transformed.n); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -104,20 +98,16 @@ * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster. * \param plane [in/out] source plane (transformed on return) * \param transform [in] transform matrix + * \warning the plane normal must be unit-length */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void TransformPlane(Plane& plane, const Matrix4x4& transform) { - // Catch the rotation part of the 4x4 matrix - Matrix3x3 Rot = transform; - - // Rotate the normal - plane.n *= Rot; + // Rotate the normal using the rotation part of the 4x4 matrix + plane.n *= Matrix3x3(transform); // Compute new d - Point Trans; - transform.GetTrans(Trans); - plane.d = (plane.d * plane.n - Trans)|plane.n; + plane.d -= Point(transform.GetTrans())|plane.n; } #endif // __ICEPLANE_H__ diff --git a/OpcodeDistrib/OPC_Point.cpp b/Ice/IcePoint.cpp similarity index 70% rename from OpcodeDistrib/OPC_Point.cpp rename to Ice/IcePoint.cpp index a84e341..9fd7570 100644 --- a/OpcodeDistrib/OPC_Point.cpp +++ b/Ice/IcePoint.cpp @@ -50,9 +50,39 @@ using namespace IceMaths; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a positive unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::PositiveUnitRandomVector() +{ + x = UnitRandomFloat(); + y = UnitRandomFloat(); + z = UnitRandomFloat(); + Normalize(); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::UnitRandomVector() +{ + x = UnitRandomFloat() - 0.5f; + y = UnitRandomFloat() - 0.5f; + z = UnitRandomFloat() - 0.5f; + Normalize(); + return *this; +} + // Cast operator // WARNING: not inlined -//Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); } +Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); } Point& Point::Refract(const Point& eye, const Point& n, float refractindex, Point& refracted) { @@ -82,64 +112,33 @@ Point& Point::ProjectToPlane(const Plane& p) return *this; } -//### could be optimized -Point& Point::Unfold(Plane& p, Point& a, Point& b) +void Point::ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const { - ASSERT(!"Obsolete code"); -/* Point v = *this; - - // Form the plane of the triangle - Plane TriPlane(a, b, v); - - // Compute rotation angle - float CosAngle = p.n|TriPlane.n; - float Angle = acosf(CosAngle); - if(FastFabs(Angle)<0.0001f) return *this; // Point is already on plane // ## project - - // Rotate around (a,b) - // 1) Move to origin - Point p0t(0.0f, 0.0f, 0.0f); - Point p1t = b - a; - Point p2t = v - a; - - // 2) Rotate so that rotation axis = Z axis - Matrix3x3 M; - Point Axis = (p1t - p0t).Normalize(); - float DpX = FastFabs(Axis.x); - float DpY = FastFabs(Axis.y); - float DpZ = FastFabs(Axis.z); - if(FastFabs((Axis|Point(0.0f, 0.0f, 1.0f)) - 1.0f) < 0.0001f) M.Identity(); - else if(FastFabs((Axis|Point(0.0f, 0.0f, 1.0f)) + 1.0f) < 0.0001f) { M.Identity(); Angle = -Angle; } - else M.MapToZ(Axis); - - Point p0r = M * p0t; - Point p1r = M * p1t; - Point p2r = M * p2t; - - // 3) Rotate around original angle - Matrix3x3 RotZ; - RotZ.RotZ(-Angle); - p0r *= RotZ; - p1r *= RotZ; - p2r *= RotZ; - - // 4) Rotate back (since M is a rotation matrix we don't invert or transpose it, just swap mul conventions) - p0t = p0r * M; - p1t = p1r * M; - p2t = p2r * M; - - // 5) Translate back - p0t += a; - p1t += a; - p2t += a; - - // 6) Check new plane - Plane pipo(p0t, p1t, p2t); - Angle = p.n|pipo.n; // Must be 1 - - *this = p2t; -*/ - return *this; + projected = HPoint(x, y, z, 1.0f) * mat; + projected.w = 1.0f / projected.w; + + projected.x*=projected.w; + projected.y*=projected.w; + projected.z*=projected.w; + + projected.x *= halfrenderwidth; projected.x += halfrenderwidth; + projected.y *= -halfrenderheight; projected.y += halfrenderheight; +} + +void Point::SetNotUsed() +{ + // We use a particular integer pattern : 0xffffffff everywhere. This is a NAN. + IR(x) = 0xffffffff; + IR(y) = 0xffffffff; + IR(z) = 0xffffffff; +} + +BOOL Point::IsNotUsed() const +{ + if(IR(x)!=0xffffffff) return FALSE; + if(IR(y)!=0xffffffff) return FALSE; + if(IR(z)!=0xffffffff) return FALSE; + return TRUE; } Point& Point::Mult(const Matrix3x3& mat, const Point& a) @@ -192,4 +191,3 @@ Point& Point::InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& z = sx * rotpos.m[0][2] + sy * rotpos.m[1][2] + sz * rotpos.m[2][2]; return *this; } - diff --git a/OpcodeDistrib/OPC_Point.h b/Ice/IcePoint.h similarity index 86% rename from OpcodeDistrib/OPC_Point.h rename to Ice/IcePoint.h index 4bb31ff..78148d6 100644 --- a/OpcodeDistrib/OPC_Point.h +++ b/Ice/IcePoint.h @@ -12,15 +12,6 @@ #ifndef __ICEPOINT_H__ #define __ICEPOINT_H__ - enum PointComponent - { - _X = 0, - _Y = 1, - _Z = 2, - _W = 3, - _FORCE_DWORD = 0x7fffffff - }; - // Forward declarations class HPoint; class Plane; @@ -38,11 +29,12 @@ //! Empty constructor inline_ Point() {} //! Constructor from a single float - inline_ Point(float val) : x(val), y(val), z(val) {} +// inline_ Point(float val) : x(val), y(val), z(val) {} +// Removed since it introduced the nasty "Point T = *Matrix4x4.GetTrans();" bug....... //! Constructor from floats inline_ Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} //! Constructor from array - inline_ Point(float f[3]) : x(f[_X]), y(f[_Y]), z(f[_Z]) {} + inline_ Point(const float f[3]) : x(f[_X]), y(f[_Y]), z(f[_Z]) {} //! Copy constructor inline_ Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} //! Destructor @@ -64,7 +56,7 @@ //! Assignment from values inline_ Point& Set(float _x, float _y, float _z) { x = _x; y = _y; z = _z; return *this; } //! Assignment from array - inline_ Point& Set(float f[3]) { x = f[_X]; y = f[_Y]; z = f[_Z]; return *this; } + inline_ Point& Set(const float f[3]) { x = f[_X]; y = f[_Y]; z = f[_Z]; return *this; } //! Assignment from another point inline_ Point& Set(const Point& src) { x = src.x; y = src.y; z = src.z; return *this; } @@ -73,7 +65,7 @@ //! Adds a vector inline_ Point& Add(float _x, float _y, float _z) { x += _x; y += _y; z += _z; return *this; } //! Adds a vector - inline_ Point& Add(float f[3]) { x += f[_X]; y += f[_Y]; z += f[_Z]; return *this; } + inline_ Point& Add(const float f[3]) { x += f[_X]; y += f[_Y]; z += f[_Z]; return *this; } //! Adds vectors inline_ Point& Add(const Point& p, const Point& q) { x = p.x+q.x; y = p.y+q.y; z = p.z+q.z; return *this; } @@ -82,7 +74,7 @@ //! Subtracts a vector inline_ Point& Sub(float _x, float _y, float _z) { x -= _x; y -= _y; z -= _z; return *this; } //! Subtracts a vector - inline_ Point& Sub(float f[3]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; return *this; } + inline_ Point& Sub(const float f[3]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; return *this; } //! Subtracts vectors inline_ Point& Sub(const Point& p, const Point& q) { x = p.x-q.x; y = p.y-q.y; z = p.z-q.z; return *this; } @@ -229,14 +221,30 @@ inline_ float Volume() const { return x * y * z; } //! Checks the point is near zero - bool ApproxZero() const { return SquareMagnitude() < EPSILON2; } + inline_ bool ApproxZero() const { return SquareMagnitude() < EPSILON2; } + + //! Tests for exact zero vector + inline_ BOOL IsZero() const + { + if(IR(x) || IR(y) || IR(z)) return FALSE; + return TRUE; + } + + //! Checks point validity + inline_ BOOL IsValid() const + { + if(!IsValidFloat(x)) return FALSE; + if(!IsValidFloat(y)) return FALSE; + if(!IsValidFloat(z)) return FALSE; + return TRUE; + } //! Slighty moves the point - void Tweak(udword coordmask, udword tweakmask) + void Tweak(udword coord_mask, udword tweak_mask) { - if(coordmask&1) { udword Dummy = IR(x); Dummy^=tweakmask; x = FR(Dummy); } - if(coordmask&2) { udword Dummy = IR(y); Dummy^=tweakmask; y = FR(Dummy); } - if(coordmask&4) { udword Dummy = IR(z); Dummy^=tweakmask; z = FR(Dummy); } + if(coord_mask&1) { udword Dummy = IR(x); Dummy^=tweak_mask; x = FR(Dummy); } + if(coord_mask&2) { udword Dummy = IR(y); Dummy^=tweak_mask; y = FR(Dummy); } + if(coord_mask&4) { udword Dummy = IR(z); Dummy^=tweak_mask; z = FR(Dummy); } } #define TWEAKMASK 0x3fffff @@ -281,6 +289,24 @@ return *this; } + //! Clamps vector length + inline_ Point& ClampLength(float limit_length) + { + if(limit_length>=0.0f) // Magnitude must be positive + { + float CurrentSquareLength = SquareMagnitude(); + + if(CurrentSquareLength > limit_length * limit_length) + { + float Coeff = limit_length / sqrtf(CurrentSquareLength); + x *= Coeff; + y *= Coeff; + z *= Coeff; + } + } + return *this; + } + //! Computes distance to another point inline_ float Distance(const Point& b) const { @@ -355,11 +381,16 @@ //! Hash function from Ville Miettinen inline_ udword GetHashValue() const - { - const udword* h = (const udword*)(this); - udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 - return (f>>22)^(f>>12)^(f); - } + { + const udword* h = (const udword*)(this); + udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 + return (f>>22)^(f>>12)^(f); + } + + //! Stuff magic values in the point, marking it as explicitely not used. + void SetNotUsed(); + //! Checks the point is marked as not used + BOOL IsNotUsed() const; // Arithmetic operators diff --git a/OpcodeDistrib/OPC_Preprocessor.h b/Ice/IcePreprocessor.h similarity index 52% rename from OpcodeDistrib/OPC_Preprocessor.h rename to Ice/IcePreprocessor.h index 8424c92..bb0ef7b 100644 --- a/OpcodeDistrib/OPC_Preprocessor.h +++ b/Ice/IcePreprocessor.h @@ -28,27 +28,32 @@ #pragma message("Compiling with unknown compiler...") #endif - // Check compiler options - #ifdef COMPILER_VISUAL_CPP - #if defined(_CHAR_UNSIGNED) - #endif + // Check compiler options. If this file is included in user-apps, this + // shouldn't be needed, so that they can use what they like best. + #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS + #ifdef COMPILER_VISUAL_CPP + #if defined(_CHAR_UNSIGNED) + #endif - #if defined(_CPPRTTI) - #error Please disable RTTI... - #endif + #if defined(_CPPRTTI) + #error Please disable RTTI... + #endif - #if defined(_CPPUNWIND) - #error Please disable exceptions... - #endif + #if defined(_CPPUNWIND) + #error Please disable exceptions... + #endif - #if defined(_MT) - // Multithreading + #if defined(_MT) + // Multithreading + #endif #endif #endif // Check debug mode #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it. - #define _DEBUG + #ifndef _DEBUG + #define _DEBUG + #endif #endif #ifdef _DEBUG @@ -58,25 +63,66 @@ #ifndef THIS_FILE #define THIS_FILE __FILE__ #endif -/* - #ifdef ICECORE_EXPORTS - #define ICECORE_API __declspec(dllexport) + + #ifndef ICE_NO_DLL + #ifdef ICECORE_EXPORTS + #define ICECORE_API __declspec(dllexport) + #else + #define ICECORE_API __declspec(dllimport) + #endif #else - #define ICECORE_API __declspec(dllimport) + #define ICECORE_API #endif -*/ + + // Don't override new/delete +// #define DEFAULT_NEWDELETE + #define DONT_TRACK_MEMORY_LEAKS + #define FUNCTION extern "C" // Cosmetic stuff [mainly useful with multiple inheritance] - #define override(baseclass) virtual + #define override(base_class) virtual // Our own inline keyword, so that: // - we can switch to __forceinline to check it's really better or not // - we can remove __forceinline if the compiler doesn't support it - #define inline_ __forceinline +// #define inline_ __forceinline // #define inline_ inline + // Contributed by Bruce Mitchener + #if defined(COMPILER_VISUAL_CPP) + #define inline_ __forceinline +// #define inline_ inline + #elif defined(__GNUC__) && __GNUC__ < 3 + #define inline_ inline + #elif defined(__GNUC__) + #define inline_ inline __attribute__ ((always_inline)) + #else + #define inline_ inline + #endif + // Down the hatch #pragma inline_depth( 255 ) + #ifdef COMPILER_VISUAL_CPP + #pragma intrinsic(memcmp) + #pragma intrinsic(memcpy) + #pragma intrinsic(memset) + #pragma intrinsic(strcat) + #pragma intrinsic(strcmp) + #pragma intrinsic(strcpy) + #pragma intrinsic(strlen) + #pragma intrinsic(abs) + #pragma intrinsic(labs) + #endif + + // ANSI compliance + #ifdef _DEBUG + // Remove painful warning in debug + inline_ bool __False__(){ return false; } + #define for if(__False__()){} else for + #else + #define for if(0){} else for + #endif + #endif // __ICEPREPROCESSOR_H__ diff --git a/Ice/IceRandom.cpp b/Ice/IceRandom.cpp new file mode 100644 index 0000000..0139be6 --- /dev/null +++ b/Ice/IceRandom.cpp @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.cpp + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +void IceCore:: SRand(udword seed) +{ + srand(seed); +} + +udword IceCore::Rand() +{ + return rand(); +} + + +static BasicRandom gRandomGenerator(42); + +udword IceCore::GetRandomIndex(udword max_index) +{ + // We don't use rand() since it's limited to RAND_MAX + udword Index = gRandomGenerator.Randomize(); + return Index % max_index; +} + diff --git a/Ice/IceRandom.h b/Ice/IceRandom.h new file mode 100644 index 0000000..3584769 --- /dev/null +++ b/Ice/IceRandom.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.h + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERANDOM_H__ +#define __ICERANDOM_H__ + + FUNCTION ICECORE_API void SRand(udword seed); + FUNCTION ICECORE_API udword Rand(); + + //! Returns a unit random floating-point value + inline_ float UnitRandomFloat() { return float(Rand()) * ONE_OVER_RAND_MAX; } + + //! Returns a random index so that 0<= index < max_index + ICECORE_API udword GetRandomIndex(udword max_index); + + class ICECORE_API BasicRandom + { + public: + + //! Constructor + inline_ BasicRandom(udword seed=0) : mRnd(seed) {} + //! Destructor + inline_ ~BasicRandom() {} + + inline_ void SetSeed(udword seed) { mRnd = seed; } + inline_ udword GetCurrentValue() const { return mRnd; } + inline_ udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; } + + private: + udword mRnd; + }; + +#endif // __ICERANDOM_H__ + diff --git a/Ice/IceRay.cpp b/Ice/IceRay.cpp new file mode 100644 index 0000000..be04043 --- /dev/null +++ b/Ice/IceRay.cpp @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Ray class. + * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity + * \class Ray + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + O = Origin = impact point + i = normalized vector along the x axis + j = normalized vector along the y axis = actually the normal vector in O + D = Direction vector, norm |D| = 1 + N = Projection of D on y axis, norm |N| = normal reaction + T = Projection of D on x axis, norm |T| = tangential reaction + R = Reflexion vector + + ^y + | + | + | + _ _ _| _ _ _ + * * *| + \ | / + \ |N / | + R\ | /D + \ | / | + \ | / + _________\|/______*_______>x + O T + + Let define theta = angle between D and N. Then cos(theta) = |N| / |D| = |N| since D is normalized. + + j|D = |j|*|D|*cos(theta) => |N| = j|D + + Then we simply have: + + D = N + T + + To compute tangential reaction : + + T = D - N + + To compute reflexion vector : + + R = N - T = N - (D-N) = 2*N - D +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +float Ray::SquareDistance(const Point& point, float* t) const +{ + Point Diff = point - mOrig; + float fT = Diff | mDir; + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + fT /= mDir.SquareMagnitude(); + Diff -= fT*mDir; + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); +} diff --git a/Ice/IceRay.h b/Ice/IceRay.h new file mode 100644 index 0000000..f99a78b --- /dev/null +++ b/Ice/IceRay.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERAY_H__ +#define __ICERAY_H__ + + class ICEMATHS_API Ray + { + public: + //! Constructor + inline_ Ray() {} + //! Constructor + inline_ Ray(const Point& orig, const Point& dir) : mOrig(orig), mDir(dir) {} + //! Copy constructor + inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {} + //! Destructor + inline_ ~Ray() {} + + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + + Point mOrig; //!< Ray origin + Point mDir; //!< Normalized direction + }; + + inline_ void ComputeReflexionVector(Point& reflected, const Point& incoming_dir, const Point& outward_normal) + { + reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal); + } + + inline_ void ComputeReflexionVector(Point& reflected, const Point& source, const Point& impact, const Point& normal) + { + Point V = impact - source; + reflected = V - normal * 2.0f * (V|normal); + } + + inline_ void DecomposeVector(Point& normal_compo, Point& tangent_compo, const Point& outward_dir, const Point& outward_normal) + { + normal_compo = outward_normal * (outward_dir|outward_normal); + tangent_compo = outward_dir - normal_compo; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a direction vector from world space to local space + * \param local_dir [out] direction vector in local space + * \param world_dir [in] direction vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalDirection(Point& local_dir, const Point& world_dir, const Matrix4x4& world) + { + // Get world direction back in local space +// Matrix3x3 InvWorld = world; +// local_dir = InvWorld * world_dir; + local_dir = Matrix3x3(world) * world_dir; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a position vector from world space to local space + * \param local_pt [out] position vector in local space + * \param world_pt [in] position vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalPoint(Point& local_pt, const Point& world_pt, const Matrix4x4& world) + { + // Get world vertex back in local space + Matrix4x4 InvWorld = world; + InvWorld.Invert(); + local_pt = world_pt * InvWorld; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a ray from world space to local space + * \param local_ray [out] ray in local space + * \param world_ray [in] ray in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalRay(Ray& local_ray, const Ray& world_ray, const Matrix4x4& world) + { + // Get world ray back in local space + ComputeLocalDirection(local_ray.mDir, world_ray.mDir, world); + ComputeLocalPoint(local_ray.mOrig, world_ray.mOrig, world); + } + +#endif // __ICERAY_H__ diff --git a/Ice/IceRevisitedRadix.cpp b/Ice/IceRevisitedRadix.cpp new file mode 100644 index 0000000..f55b0cf --- /dev/null +++ b/Ice/IceRevisitedRadix.cpp @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains source code from the article "Radix Sort Revisited". + * \file IceRevisitedRadix.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Revisited Radix Sort. + * This is my new radix routine: + * - it uses indices and doesn't recopy the values anymore, hence wasting less ram + * - it creates all the histograms in one run instead of four + * - it sorts words faster than dwords and bytes faster than words + * - it correctly sorts negative floating-point values by patching the offsets + * - it automatically takes advantage of temporal coherence + * - multiple keys support is a side effect of temporal coherence + * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway] + * + * History: + * - 08.15.98: very first version + * - 04.04.00: recoded for the radix article + * - 12.xx.00: code lifting + * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here) + * - 10.11.01: added local ram support + * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting...... + * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all. + * - ranks are not "reset" anymore, but implicit on first calls + * - 07.05.02: - offsets rewritten with one less indirection. + * - 11.03.02: - "bool" replaced with RadixHint enum + * + * \class RadixSort + * \author Pierre Terdiman + * \version 1.4 + * \date August, 15, 1998 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +To do: + - add an offset parameter between two input values (avoid some data recopy sometimes) + - unroll ? asm ? + - 11 bits trick & 3 passes as Michael did + - prefetch stuff the day I have a P3 + - make a version with 16-bits indices ? +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +#define INVALIDATE_RANKS mCurrentSize|=0x80000000 +#define VALIDATE_RANKS mCurrentSize&=0x7fffffff +#define CURRENT_SIZE (mCurrentSize&0x7fffffff) +#define INVALID_RANKS (mCurrentSize&0x80000000) + +#define CHECK_RESIZE(n) \ + if(n!=mPreviousSize) \ + { \ + if(n>mCurrentSize) Resize(n); \ + else ResetRanks(); \ + mPreviousSize = n; \ + } + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + ZeroMemory(mHistogram, 256*4*sizeof(udword)); \ + \ + /* Prepare to count */ \ + ubyte* p = (ubyte*)input; \ + ubyte* pe = &p[nb*4]; \ + udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \ + udword* h1= &mHistogram[256]; /* Histogram for second pass */ \ + udword* h2= &mHistogram[512]; /* Histogram for third pass */ \ + udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + type* Running = (type*)buffer; \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(ValCurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Resize lists if needed + CheckResize(nb); + +#ifdef RADIX_LOCAL_RAM + // Allocate histograms & offsets on the stack + udword mHistogram[256*4]; +// udword mOffset[256]; + udword* mLink[256]; +#endif + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); } + else { CREATE_HISTOGRAMS(sdword, input); } + + // Compute #negative values involved if needed + udword NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + udword* h3= &mHistogram[768]; + for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(udword j=0;j<4;j++) + { + CHECK_PASS_VALIDITY(j); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(PerformPass) + { + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets +// mOffset[0] = 0; +// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + mLink[0] = mRanks2; + for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well +// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones + mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones +// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values +// mOffset[128] = 0; + mLink[128] = mRanks2; +// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + ubyte* InputBytes = (ubyte*)input; + InputBytes += j; + if(INVALID_RANKS) + { +// for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above + else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above + else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(udword i=0;i=SqrLen) + { + fT = 1.0f; + Diff -= Dir; + } + else + { + fT /= SqrLen; + Diff -= fT*Dir; + } + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); } diff --git a/OpcodeDistrib/OPC_Ray.h b/Ice/IceSegment.h similarity index 69% rename from OpcodeDistrib/OPC_Ray.h rename to Ice/IceSegment.h index a61fc61..3dc8314 100644 --- a/OpcodeDistrib/OPC_Ray.h +++ b/Ice/IceSegment.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains code for rays. - * \file IceRay.h + * Contains code for segments. + * \file IceSegment.h * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -9,24 +9,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICERAY_H__ -#define __ICERAY_H__ - - class ICEMATHS_API Ray - { - public: - //! Constructor - inline_ Ray() {} - //! Constructor - inline_ Ray(const Point& orig, const Point& dir) : mOrig(orig), mDir(dir) {} - //! Copy constructor - inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {} - //! Destructor - inline_ ~Ray() {} - - Point mOrig; //!< Ray origin - Point mDir; //!< Normalized direction - }; +#ifndef __ICESEGMENT_H__ +#define __ICESEGMENT_H__ class ICEMATHS_API Segment { @@ -40,14 +24,17 @@ //! Destructor inline_ ~Segment() {} - inline_ const Point& GetOrigin() const { return mP0; } - inline_ Point ComputeDirection() const { return mP1 - mP0; } + inline_ const Point& GetOrigin() const { return mP0; } + inline_ Point ComputeDirection() const { return mP1 - mP0; } + inline_ void ComputeDirection(Point& dir) const { dir = mP1 - mP0; } + inline_ float ComputeLength() const { return mP1.Distance(mP0); } + inline_ float ComputeSquareLength() const { return mP1.SquareDistance(mP0); } inline_ void SetOriginDirection(const Point& origin, const Point& direction) - { - mP0 = mP1 = origin; - mP1 += direction; - } + { + mP0 = mP1 = origin; + mP1 += direction; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -58,8 +45,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void ComputePoint(Point& pt, float t) const { pt = mP0 + t * (mP1 - mP0); } + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + Point mP0; //!< Start of segment Point mP1; //!< End of segment }; -#endif // __ICERAY_H__ +#endif // __ICESEGMENT_H__ diff --git a/Ice/IceTriangle.cpp b/Ice/IceTriangle.cpp new file mode 100644 index 0000000..ef89109 --- /dev/null +++ b/Ice/IceTriangle.cpp @@ -0,0 +1,286 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a triangle class. + * + * \class Tri + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static sdword VPlaneSideEps(const Point& v, const Plane& plane, float epsilon) +{ + // Compute distance from current vertex to the plane + float Dist = plane.Distance(v); + // Compute side: + // 1 = the vertex is on the positive side of the plane + // -1 = the vertex is on the negative side of the plane + // 0 = the vertex is on the plane (within epsilon) + return Dist > epsilon ? 1 : Dist < -epsilon ? -1 : 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Flip() +{ + Point Tmp = mVerts[1]; + mVerts[1] = mVerts[2]; + mVerts[2] = Tmp; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Area() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return ((p0 - p1)^(p0 - p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Perimeter() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Compacity() const +{ + float P = Perimeter(); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area()/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Normal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::DenormalizedNormal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Center(Point& center) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + center = (p0 + p1 + p2)*INV3; +} + +PartVal Triangle::TestAgainstPlane(const Plane& plane, float epsilon) const +{ + bool Pos = false, Neg = false; + + // Loop through all vertices + for(udword i=0;i<3;i++) + { + // Compute side: + sdword Side = VPlaneSideEps(mVerts[i], plane, epsilon); + + if (Side < 0) Neg = true; + else if (Side > 0) Pos = true; + } + + if (!Pos && !Neg) return TRI_ON_PLANE; + else if (Pos && Neg) return TRI_INTERSECT; + else if (Pos && !Neg) return TRI_PLUS_SPACE; + else if (!Pos && Neg) return TRI_MINUS_SPACE; + + // What?! + return TRI_FORCEDWORD; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle moment. + * \param m [out] the moment + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* +void Triangle::ComputeMoment(Moment& m) +{ + // Compute the area of the triangle + m.mArea = Area(); + + // Compute the centroid + Center(m.mCentroid); + + // Second-order components. Handle zero-area faces. + Point& p = mVerts[0]; + Point& q = mVerts[1]; + Point& r = mVerts[2]; + if(m.mArea==0.0f) + { + // This triangle has zero area. The second order components would be eliminated with the usual formula, so, for the + // sake of robustness we use an alternative form. These are the centroid and second-order components of the triangle's vertices. + m.mCovariance.m[0][0] = (p.x*p.x + q.x*q.x + r.x*r.x); + m.mCovariance.m[0][1] = (p.x*p.y + q.x*q.y + r.x*r.y); + m.mCovariance.m[0][2] = (p.x*p.z + q.x*q.z + r.x*r.z); + m.mCovariance.m[1][1] = (p.y*p.y + q.y*q.y + r.y*r.y); + m.mCovariance.m[1][2] = (p.y*p.z + q.y*q.z + r.y*r.z); + m.mCovariance.m[2][2] = (p.z*p.z + q.z*q.z + r.z*r.z); + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } + else + { + const float OneOverTwelve = 1.0f / 12.0f; + m.mCovariance.m[0][0] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.x + p.x*p.x + q.x*q.x + r.x*r.x) * OneOverTwelve; + m.mCovariance.m[0][1] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.y + p.x*p.y + q.x*q.y + r.x*r.y) * OneOverTwelve; + m.mCovariance.m[1][1] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.y + p.y*p.y + q.y*q.y + r.y*r.y) * OneOverTwelve; + m.mCovariance.m[0][2] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.z + p.x*p.z + q.x*q.z + r.x*r.z) * OneOverTwelve; + m.mCovariance.m[1][2] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.z + p.y*p.z + q.y*q.z + r.y*r.z) * OneOverTwelve; + m.mCovariance.m[2][2] = m.mArea * (9.0f * m.mCentroid.z*m.mCentroid.z + p.z*p.z + q.z*q.z + r.z*r.z) * OneOverTwelve; + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } +} +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MinEdgeLength() const +{ + float Min = MAX_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MaxEdgeLength() const +{ + float Max = MIN_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::ComputePoint(float u, float v, Point& pt, udword* nearvtx) const +{ + // Compute point coordinates + pt = (1.0f - u - v)*mVerts[0] + u*mVerts[1] + v*mVerts[2]; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(mVerts[0].SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + mVerts[1].SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + mVerts[2].SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = d.SmallestAxis(); + } +} + +void Triangle::Inflate(float fat_coeff, bool constant_border) +{ + // Compute triangle center + Point TriangleCenter; + Center(TriangleCenter); + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(udword i=0;i<3;i++) + { + Point v = mVerts[i] - TriangleCenter; + + if(constant_border) v.Normalize(); + + mVerts[i] += v * fat_coeff; + } +} diff --git a/Ice/IceTriangle.h b/Ice/IceTriangle.h new file mode 100644 index 0000000..02e03ce --- /dev/null +++ b/Ice/IceTriangle.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRIANGLE_H__ +#define __ICETRIANGLE_H__ + + // Forward declarations + class Moment; + + // Partitioning values + enum PartVal + { + TRI_MINUS_SPACE = 0, //!< Triangle is in the negative space + TRI_PLUS_SPACE = 1, //!< Triangle is in the positive space + TRI_INTERSECT = 2, //!< Triangle intersects plane + TRI_ON_PLANE = 3, //!< Triangle and plane are coplanar + + TRI_FORCEDWORD = 0x7fffffff + }; + + // A triangle class. + class ICEMATHS_API Triangle + { + public: + //! Constructor + inline_ Triangle() {} + //! Constructor + inline_ Triangle(const Point& p0, const Point& p1, const Point& p2) { mVerts[0]=p0; mVerts[1]=p1; mVerts[2]=p2; } + //! Copy constructor + inline_ Triangle(const Triangle& triangle) + { + mVerts[0] = triangle.mVerts[0]; + mVerts[1] = triangle.mVerts[1]; + mVerts[2] = triangle.mVerts[2]; + } + //! Destructor + inline_ ~Triangle() {} + //! Vertices + Point mVerts[3]; + + // Methods + void Flip(); + float Area() const; + float Perimeter() const; + float Compacity() const; + void Normal(Point& normal) const; + void DenormalizedNormal(Point& normal) const; + void Center(Point& center) const; + inline_ Plane PlaneEquation() const { return Plane(mVerts[0], mVerts[1], mVerts[2]); } + + PartVal TestAgainstPlane(const Plane& plane, float epsilon) const; +// float Distance(Point& cp, Point& cq, Tri& tri); + void ComputeMoment(Moment& m); + float MinEdgeLength() const; + float MaxEdgeLength() const; + void ComputePoint(float u, float v, Point& pt, udword* nearvtx=null) const; + void Inflate(float fat_coeff, bool constant_border); + }; + +#endif // __ICETRIANGLE_H__ diff --git a/Ice/IceTrilist.h b/Ice/IceTrilist.h new file mode 100644 index 0000000..7a8d9de --- /dev/null +++ b/Ice/IceTrilist.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a triangle container. + * \file IceTrilist.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRILIST_H__ +#define __ICETRILIST_H__ + + class ICEMATHS_API TriList : public Container + { + public: + // Constructor / Destructor + TriList() {} + ~TriList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/9; } + inline_ Triangle* GetTriangles() const { return (Triangle*)GetEntries(); } + + void AddTri(const Triangle& tri) + { + Add(tri.mVerts[0].x).Add(tri.mVerts[0].y).Add(tri.mVerts[0].z); + Add(tri.mVerts[1].x).Add(tri.mVerts[1].y).Add(tri.mVerts[1].z); + Add(tri.mVerts[2].x).Add(tri.mVerts[2].y).Add(tri.mVerts[2].z); + } + + void AddTri(const Point& p0, const Point& p1, const Point& p2) + { + Add(p0.x).Add(p0.y).Add(p0.z); + Add(p1.x).Add(p1.y).Add(p1.z); + Add(p2.x).Add(p2.y).Add(p2.z); + } + }; + + class ICEMATHS_API TriangleList : public Container + { + public: + // Constructor / Destructor + TriangleList() {} + ~TriangleList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/3; } + inline_ IndexedTriangle* GetTriangles() const { return (IndexedTriangle*)GetEntries();} + + void AddTriangle(const IndexedTriangle& tri) + { + Add(tri.mVRef[0]).Add(tri.mVRef[1]).Add(tri.mVRef[2]); + } + + void AddTriangle(udword vref0, udword vref1, udword vref2) + { + Add(vref0).Add(vref1).Add(vref2); + } + }; + +#endif //__ICETRILIST_H__ diff --git a/Ice/IceTypes.h b/Ice/IceTypes.h new file mode 100644 index 0000000..dac0a71 --- /dev/null +++ b/Ice/IceTypes.h @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains custom types. + * \file IceTypes.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETYPES_H__ +#define __ICETYPES_H__ + + #define USE_HANDLE_MANAGER + + // Constants + #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI + #define HALFPI 1.57079632679489661923f //!< 0.5 * PI + #define TWOPI 6.28318530717958647692f //!< 2.0 * PI + #define INVPI 0.31830988618379067154f //!< 1.0 / PI + + #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees + #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians + + #define EXP 2.71828182845904523536f //!< e + #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2) + #define LN2 0.693147180559945f //!< ln(2) + #define INVLN2 1.44269504089f //!< 1.0f / ln(2) + + #define INV3 0.33333333333333333333f //!< 1/3 + #define INV6 0.16666666666666666666f //!< 1/6 + #define INV7 0.14285714285714285714f //!< 1/7 + #define INV9 0.11111111111111111111f //!< 1/9 + #define INV255 0.00392156862745098039f //!< 1/255 + + #define SQRT2 1.41421356237f //!< sqrt(2) + #define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + + #define SQRT3 1.73205080757f //!< sqrt(3) + #define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + + #define null 0 //!< our own NULL pointer + + // Custom types used in ICE + typedef signed char sbyte; //!< sizeof(sbyte) must be 1 + typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 + typedef signed short sword; //!< sizeof(sword) must be 2 + typedef unsigned short uword; //!< sizeof(uword) must be 2 + typedef signed int sdword; //!< sizeof(sdword) must be 4 + typedef unsigned int udword; //!< sizeof(udword) must be 4 + typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 + typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 + typedef float float32; //!< sizeof(float32) must be 4 + typedef double float64; //!< sizeof(float64) must be 4 + + ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 ! + ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8); + ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8); + + //! TO BE DOCUMENTED + #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name + + typedef udword DynID; //!< Dynamic identifier +#ifdef USE_HANDLE_MANAGER + typedef udword KID; //!< Kernel ID +// DECLARE_ICE_HANDLE(KID); +#else + typedef uword KID; //!< Kernel ID +#endif + typedef udword RTYPE; //!< Relationship-type (!) between owners and references + #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers) +#ifdef USE_HANDLE_MANAGER + #define INVALID_KID 0xffffffff //!< Invalid Kernel ID +#else + #define INVALID_KID 0xffff //!< Invalid Kernel ID +#endif + #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value + + // Define BOOL if needed + #ifndef BOOL + typedef int BOOL; //!< Another boolean type. + #endif + + //! Union of a float and a sdword + typedef union { + float f; //!< The float + sdword d; //!< The integer + }scell; + + //! Union of a float and a udword + typedef union { + float f; //!< The float + udword d; //!< The integer + }ucell; + + // Type ranges + #define MAX_SBYTE 0x7f //!< max possible sbyte value + #define MIN_SBYTE 0x80 //!< min possible sbyte value + #define MAX_UBYTE 0xff //!< max possible ubyte value + #define MIN_UBYTE 0x00 //!< min possible ubyte value + #define MAX_SWORD 0x7fff //!< max possible sword value + #define MIN_SWORD 0x8000 //!< min possible sword value + #define MAX_UWORD 0xffff //!< max possible uword value + #define MIN_UWORD 0x0000 //!< min possible uword value + #define MAX_SDWORD 0x7fffffff //!< max possible sdword value + #define MIN_SDWORD 0x80000000 //!< min possible sdword value + #define MAX_UDWORD 0xffffffff //!< max possible udword value + #define MIN_UDWORD 0x00000000 //!< min possible udword value + #define MAX_FLOAT FLT_MAX //!< max possible float value + #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0 + #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT + #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT + #define IEEE_UNDERFLOW_LIMIT 0x1a000000 + + #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand() + + typedef int (__stdcall* PROC)(); //!< A standard procedure call. + typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call + typedef void** VTABLE; //!< A V-Table. + + #undef MIN + #undef MAX + #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b + #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b + #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c + + template inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; } + template inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; } + template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } + template inline_ void TSetMax (T& a, const T& b) { if(a> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); + n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); + n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); + n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); + n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + } + + //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection) + inline_ udword CountBits(udword n) + { + // This relies of the fact that the count of n bits can NOT overflow + // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts + // 2 bit interger, 3 bit count requires only a 2 bit interger. + // So we add all bit pairs, then each nible, then each byte etc... + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + return n; + } + + //! Even faster? + inline_ udword CountBits2(udword bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333); + bits = ((bits >> 4) + bits) & 0x0F0F0F0F; + return (bits * 0x01010101) >> 24; + } + + //! Spread out bits. EG 00001111 -> 0101010101 + //! 00001010 -> 0100010000 + //! This is used to interleve to intergers to produce a `Morten Key' + //! used in Space Filling Curves (See DrDobbs Journal, July 1999) + //! Order is important. + inline_ void SpreadBits(udword& n) + { + n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16); + n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8); + n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4); + n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2); + n = ( n & 0x11111111) | (( n & 0x22222222) << 1); + } + + // Next Largest Power of 2 + // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm + // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with + // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next + // largest power of 2. For a 32-bit value: + inline_ udword nlpo2(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; + } + + //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection) + inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); } + + //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection) + inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); } + + //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection) + inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<> 31; return (x^y)-y; } + + //!< Alternative min function + inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); } + + // Determine if one of the bytes in a 4 byte word is zero + inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } + + // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0 + inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } +// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } + + // Most Significant 1 Bit + // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set) + // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits. + // This process yields a bit vector with the same most significant 1 as x, but all 1's below it. + // Bitwise AND of the original value with the complement of the "folded" value shifted down by one + // yields the most significant bit. For a 32-bit value: + inline_ udword msb32(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x & ~(x >> 1)); + } + + /* + "Just call it repeatedly with various input values and always with the same variable as "memory". + The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1 + does no filtering at all. + + I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed + to the more typical FIR (Finite Impulse Response). + + Also, I'd say that you can make more intelligent and interesting filters than this, for example filters + that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter + to be applied before this one, of course." + + (JCAB on Flipcode) + */ + inline_ float FeedbackFilter(float val, float& memory, float sharpness) + { + ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter"); + if(sharpness<0.0f) sharpness = 0.0f; + else if(sharpness>1.0f) sharpness = 1.0f; + return memory = val * sharpness + memory * (1.0f - sharpness); + } + + //! If you can guarantee that your input domain (i.e. value of x) is slightly + //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the + //! following code to clamp the resulting value into [-32768,+32767] range: + inline_ int ClampToInt16(int x) + { +// ASSERT(abs(x) < (int)((1<<31u)-32767)); + + int delta = 32767 - x; + x += (delta>>31) & delta; + delta = x + 32768; + x -= (delta>>31) & delta; + return x; + } + + // Generic functions + template inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; } + template inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((xhi) ? hi : x); } + + template inline_ void TSort(Type& a, Type& b) + { + if(a>b) TSwap(a, b); + } + + template inline_ void TSort(Type& a, Type& b, Type& c) + { + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + } + + // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom) +// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); } + // ... actually this is better ! + #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object); + + //! TO BE DOCUMENTED + #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member) + //! TO BE DOCUMENTED + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns the alignment of the input address. + * \fn Alignment() + * \param address [in] address to check + * \return the best alignment (e.g. 1 for odd addresses, etc) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + FUNCTION ICECORE_API udword Alignment(udword address); + + #define IS_ALIGNED_2(x) ((x&1)==0) + #define IS_ALIGNED_4(x) ((x&3)==0) + #define IS_ALIGNED_8(x) ((x&7)==0) + + inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } + + // Compute implicit coords from an index: + // The idea is to get back 2D coords from a 1D index. + // For example: + // + // 0 1 2 ... nbu-1 + // nbu nbu+1 i ... + // + // We have i, we're looking for the equivalent (u=2, v=1) location. + // i = u + v*nbu + // <=> i/nbu = u/nbu + v + // Since 0 <= u < nbu, u/nbu = 0 (integer) + // Hence: v = i/nbu + // Then we simply put it back in the original equation to compute u = i - v*nbu + inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu) + { + v = i / nbu; + u = i - (v * nbu); + } + + // In 3D: i = u + v*nbu + w*nbu*nbv + // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w + // u/(nbu*nbv) is null since u/nbu was null already. + // v/nbv is null as well for the same reason. + // Hence w = i/(nbu*nbv) + // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu + inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv) + { + w = i / (nbu_nbv); + Compute2DCoords(u, v, i - (w * nbu_nbv), nbu); + } + +#endif // __ICEUTILS_H__ diff --git a/OpcodeDistrib/OPC_AABBCollider.cpp b/OPC_AABBCollider.cpp similarity index 56% rename from OpcodeDistrib/OPC_AABBCollider.cpp rename to OPC_AABBCollider.cpp index c50c75c..7d9346e 100644 --- a/OpcodeDistrib/OPC_AABBCollider.cpp +++ b/OPC_AABBCollider.cpp @@ -21,7 +21,7 @@ * * \class AABBCollider * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date January, 1st, 2002 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -35,36 +35,23 @@ using namespace Opcode; #include "OPC_BoxBoxOverlap.h" #include "OPC_TriBoxOverlap.h" +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + //! AABB-triangle test -#ifdef OPC_USE_CALLBACKS - #define AABB_PRIM(primindex, flag) \ - /* Request vertices from the app */ \ - VertexPointers VP; (mObjCallback)(primindex, VP, mUserData); \ - mLeafVerts[0] = *VP.Vertex[0]; \ - mLeafVerts[1] = *VP.Vertex[1]; \ - mLeafVerts[2] = *VP.Vertex[2]; \ - /* Perform triangle-box overlap test */ \ - if(TriBoxOverlap()) \ - { \ - /* Set contact status */ \ - mFlags |= flag; \ - mTouchedPrimitives->Add(primindex); \ - } -#else - #define AABB_PRIM(primindex, flag) \ - /* Direct access to vertices */ \ - const IndexedTriangle* T = &mFaces[primindex]; \ - mLeafVerts[0] = mVerts[T->mVRef[0]]; \ - mLeafVerts[1] = mVerts[T->mVRef[1]]; \ - mLeafVerts[2] = mVerts[T->mVRef[2]]; \ - /* Perform triangle-box overlap test */ \ - if(TriBoxOverlap()) \ - { \ - /* Set contact status */ \ - mFlags |= flag; \ - mTouchedPrimitives->Add(primindex); \ - } -#endif +#define AABB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index);\ + mLeafVerts[0] = *VP.Vertex[0]; \ + mLeafVerts[1] = *VP.Vertex[1]; \ + mLeafVerts[2] = *VP.Vertex[2]; \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -84,23 +71,12 @@ AABBCollider::~AABBCollider() { } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Validates current settings. You should call this method after all the settings and callbacks have been defined. - * \return null if everything is ok, else a string describing the problem - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const char* AABBCollider::ValidateSettings() -{ - return VolumeCollider::ValidateSettings(); -} - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a box cache * \param box [in] collision AABB in world space @@ -109,22 +85,61 @@ const char* AABBCollider::ValidateSettings() * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, OPCODE_Model* model) +bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model) { // Checkings - if(!model) return false; + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; - // Simple double-dispatch - if(!model->HasLeafNodes()) + if(!model.HasLeafNodes()) { - if(model->IsQuantized()) return Collide(cache, box, (const AABBQuantizedNoLeafTree*)model->GetTree()); - else return Collide(cache, box, (const AABBNoLeafTree*)model->GetTree()); + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } else { - if(model->IsQuantized()) return Collide(cache, box, (const AABBQuantizedTree*)model->GetTree()); - else return Collide(cache, box, (const AABBCollisionTree*)model->GetTree()); + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } + return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -135,7 +150,7 @@ bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, OPCODE_Mo * * \param cache [in/out] a box cache * \param box [in] AABB in world space - * \return contact status + * \return TRUE if we can return immediately */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) @@ -149,7 +164,23 @@ BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) // 3) Setup destination pointer mTouchedPrimitives = &cache.TouchedPrimitives; - // 4) Check temporal coherence : + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + AABB_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : if(TemporalCoherenceEnabled()) { // Here we use temporal coherence @@ -169,6 +200,9 @@ BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) // Perform overlap test between the cached triangle and the box (and set contact status if needed) AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; } // else no face has been touched during previous query // => we'll have to perform a normal query @@ -176,13 +210,16 @@ BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) else { // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): - if(mBox.IsInside(cache.FatBox)) + if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox)) { // - if N is included in P, return previous list // => we simply leave the list (mTouchedFaces) unchanged // Set contact status if needed if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; } else { @@ -206,133 +243,10 @@ BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) } // 5) Precompute min & max bounds if needed - if(!GetContactStatus()) - { - mMin = box.mCenter - box.mExtents; - mMax = box.mCenter + box.mExtents; - } - - return GetContactStatus(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for normal trees. - * \param cache [in/out] a box cache - * \param box [in] collision AABB in world space - * \param tree [in] model's AABB tree - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBCollisionTree* tree) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for no-leaf trees. - * \param cache [in/out] a box cache - * \param box [in] collision AABB in world space - * \param tree [in] model's AABB tree - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBNoLeafTree* tree) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized trees. - * \param cache [in/out] a box cache - * \param box [in] collision AABB in world space - * \param tree [in] model's AABB tree - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBQuantizedTree* tree) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box)) return true; + mMin = box.mCenter - box.mExtents; + mMax = box.mCenter + box.mExtents; - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized no-leaf trees. - * \param cache [in/out] a box cache - * \param box [in] collision AABB in world space - * \param tree [in] model's AABB tree - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBQuantizedNoLeafTree* tree) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box)) return true; - - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; + return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -384,7 +298,7 @@ inline_ BOOL AABBCollider::AABBContainsBox(const Point& bc, const Point& be) return TRUE; } -#define TEST_AABB_IN_BOX(center, extents) \ +#define TEST_BOX_IN_AABB(center, extents) \ if(AABBContainsBox(center, extents)) \ { \ /* Set contact status */ \ @@ -404,7 +318,7 @@ void AABBCollider::_Collide(const AABBCollisionNode* node) // Perform AABB-AABB overlap test if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; - TEST_AABB_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) if(node->IsLeaf()) { @@ -420,6 +334,33 @@ void AABBCollider::_Collide(const AABBCollisionNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized AABB trees. @@ -429,14 +370,14 @@ void AABBCollider::_Collide(const AABBCollisionNode* node) void AABBCollider::_Collide(const AABBQuantizedNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform AABB-AABB overlap test if(!AABBAABBOverlap(Extents, Center)) return; - TEST_AABB_IN_BOX(Center, Extents) + TEST_BOX_IN_AABB(Center, Extents) if(node->IsLeaf()) { @@ -452,6 +393,38 @@ void AABBCollider::_Collide(const AABBQuantizedNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for no-leaf AABB trees. @@ -463,17 +436,39 @@ void AABBCollider::_Collide(const AABBNoLeafNode* node) // Perform AABB-AABB overlap test if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; - TEST_AABB_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) - if(node->HasLeaf()) { AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) } + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { AABB_PRIM(node->GetPrimitive2(), OPC_CONTACT) } + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized no-leaf AABB trees. @@ -483,24 +478,51 @@ void AABBCollider::_Collide(const AABBNoLeafNode* node) void AABBCollider::_Collide(const AABBQuantizedNoLeafNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform AABB-AABB overlap test if(!AABBAABBOverlap(Extents, Center)) return; - TEST_AABB_IN_BOX(Center, Extents) + TEST_BOX_IN_AABB(Center, Extents) - if(node->HasLeaf()) { AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) } + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { AABB_PRIM(node->GetPrimitive2(), OPC_CONTACT) } + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for vanilla AABB trees. @@ -515,19 +537,160 @@ void AABBCollider::_Collide(const AABBTreeNode* node) node->GetAABB()->GetExtents(Extents); if(!AABBAABBOverlap(Center, Extents)) return; - if(node->IsLeaf()) + if(node->IsLeaf() || AABBContainsBox(Center, Extents)) { + mFlags |= OPC_CONTACT; mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); } else { - if(AABBContainsBox(Center, Extents)) + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::HybridAABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::~HybridAABBCollider() +{ +} + +bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;iAdd(node->GetPrimitives(), node->GetNbPrimitives()); - return; + AABB_PRIM(i, OPC_CONTACT) } + return true; + } - _Collide(node->GetPos()); - _Collide(node->GetNeg()); + // Override destination array since we're only going to get leaf boxes here + mTouchedBoxes.Reset(); + mTouchedPrimitives = &mTouchedBoxes; + + // Now, do the actual query against leaf boxes + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; } diff --git a/OpcodeDistrib/OPC_AABBCollider.h b/OPC_AABBCollider.h similarity index 75% rename from OpcodeDistrib/OPC_AABBCollider.h rename to OPC_AABBCollider.h index 40bfda8..8e2b3e4 100644 --- a/OpcodeDistrib/OPC_AABBCollider.h +++ b/OPC_AABBCollider.h @@ -40,14 +40,13 @@ // Constructor / Destructor AABBCollider(); virtual ~AABBCollider(); - // Generic collision query /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a box cache * \param box [in] collision AABB in world space @@ -56,24 +55,9 @@ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Collide(AABBCache& cache, const CollisionAABB& box, OPCODE_Model* model); - - // Collision queries - bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBCollisionTree* tree); - bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBNoLeafTree* tree); - bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBQuantizedTree* tree); - bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBQuantizedNoLeafTree* tree); + bool Collide(AABBCache& cache, const CollisionAABB& box, const Model& model); + // bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree); - // Settings - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. - * \return null if everything is ok, else a string describing the problem - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - override(Collider) const char* ValidateSettings(); - protected: CollisionAABB mBox; //!< Query box in (center, extents) form Point mMin; //!< Query box min point @@ -86,6 +70,10 @@ void _Collide(const AABBQuantizedNode* node); void _Collide(const AABBQuantizedNoLeafNode* node); void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); // Overlap tests inline_ BOOL AABBContainsBox(const Point& bc, const Point& be); inline_ BOOL AABBAABBOverlap(const Point& b, const Point& Pb); @@ -94,4 +82,16 @@ BOOL InitQuery(AABBCache& cache, const CollisionAABB& box); }; + class OPCODE_API HybridAABBCollider : public AABBCollider + { + public: + // Constructor / Destructor + HybridAABBCollider(); + virtual ~HybridAABBCollider(); + + bool Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model); + protected: + Container mTouchedBoxes; + }; + #endif // __OPC_AABBCOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_AABBTree.cpp b/OPC_AABBTree.cpp similarity index 64% rename from OpcodeDistrib/OPC_AABBTree.cpp rename to OPC_AABBTree.cpp index 544d125..5738f9b 100644 --- a/OpcodeDistrib/OPC_AABBTree.cpp +++ b/OPC_AABBTree.cpp @@ -21,7 +21,7 @@ * * \class AABBTreeNode * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -37,7 +37,7 @@ * * \class AABBTree * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -53,8 +53,17 @@ using namespace Opcode; * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -AABBTreeNode::AABBTreeNode() : mP(null), mN(null), mNbPrimitives(0), mNodePrimitives(null) +AABBTreeNode::AABBTreeNode() : + mPos (null), +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg (null), +#endif + mNbPrimitives (0), + mNodePrimitives (null) { +#ifdef OPC_USE_TREE_COHERENCE + mBitmask = 0; +#endif } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -64,8 +73,15 @@ AABBTreeNode::AABBTreeNode() : mP(null), mN(null), mNbPrimitives(0), mNodePrimit /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// AABBTreeNode::~AABBTreeNode() { - DELETESINGLE(mP); - DELETESINGLE(mN); + // Opcode 1.3: + const AABBTreeNode* Pos = GetPos(); + const AABBTreeNode* Neg = GetNeg(); +#ifndef OPC_NO_NEG_VANILLA_TREE + if(!(mPos&1)) DELETESINGLE(Pos); + if(!(mNeg&1)) DELETESINGLE(Neg); +#else + if(!(mPos&1)) DELETEARRAY(Pos); +#endif mNodePrimitives = null; // This was just a shortcut to the global list => no release mNbPrimitives = 0; } @@ -136,15 +152,16 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Checkings if(!builder) return false; - // Stop subdividing if we reach a leaf node + // Stop subdividing if we reach a leaf node. This is always performed here, + // else we could end in trouble if user overrides this. if(mNbPrimitives==1) return true; - // Check the user-defined limit - if(mNbPrimitives<=builder->mLimit) return true; + // Let the user validate the subdivision + if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true; bool ValidSplit = true; // Optimism... udword NbPos; - if(builder->mRules&SPLIT_LARGESTAXIS) + if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS) { // Find the largest axis to split along Point Extents; mBV.GetExtents(Extents); // Box extents @@ -156,7 +173,7 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Check split validity if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; } - else if(builder->mRules&SPLIT_SPLATTERPOINTS) + else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS) { // Compute the means Point Means(0.0f, 0.0f, 0.0f); @@ -171,7 +188,7 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Compute variances Point Vars(0.0f, 0.0f, 0.0f); - for(i=0;iGetSplittingValue(Index, 0); @@ -192,7 +209,7 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Check split validity if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; } - else if(builder->mRules&SPLIT_BALANCED) + else if(builder->mSettings.mRules & SPLIT_BALANCED) { // Test 3 axis, take the best float Results[3]; @@ -212,7 +229,7 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Check split validity if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; } - else if(builder->mRules&SPLIT_BESTAXIS) + else if(builder->mSettings.mRules & SPLIT_BEST_AXIS) { // Test largest, then middle, then smallest axis... @@ -244,7 +261,7 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) else ValidSplit = true; } } - else if(builder->mRules&SPLIT_FIFTY) + else if(builder->mSettings.mRules & SPLIT_FIFTY) { // Don't even bother splitting (mainly a performance test) NbPos = mNbPrimitives>>1; @@ -257,7 +274,8 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) // Here, all boxes lie in the same sub-space. Two strategies: // - if the tree *must* be complete, make an arbitrary 50-50 split // - else stop subdividing - if(builder->mRules&SPLIT_COMPLETE) +// if(builder->mSettings.mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) { builder->IncreaseNbInvalidSplits(); NbPos = mNbPrimitives>>1; @@ -266,17 +284,42 @@ bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) } // Now create children and assign their pointers. - mP = new AABBTreeNode; CHECKALLOC(mP); - mN = new AABBTreeNode; CHECKALLOC(mN); + if(builder->mNodeBase) + { + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + AABBTreeNode* Pool = (AABBTreeNode*)builder->mNodeBase; + udword Count = builder->GetCount() - 1; // Count begins to 1... + // Set last bit to tell it shouldn't be freed ### pretty ugly, find a better way. Maybe one bit in mNbPrimitives + ASSERT(!(udword(&Pool[Count+0])&1)); + ASSERT(!(udword(&Pool[Count+1])&1)); + mPos = udword(&Pool[Count+0])|1; +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg = udword(&Pool[Count+1])|1; +#endif + } + else + { + // Non-complete trees and/or Opcode 1.2 allocate nodes on-the-fly +#ifndef OPC_NO_NEG_VANILLA_TREE + mPos = (udword)new AABBTreeNode; CHECKALLOC(mPos); + mNeg = (udword)new AABBTreeNode; CHECKALLOC(mNeg); +#else + AABBTreeNode* PosNeg = new AABBTreeNode[2]; + CHECKALLOC(PosNeg); + mPos = (udword)PosNeg; +#endif + } // Update stats builder->IncreaseCount(2); // Assign children - mP->mNodePrimitives = &mNodePrimitives[0]; - mP->mNbPrimitives = NbPos; - mN->mNodePrimitives = &mNodePrimitives[NbPos]; - mN->mNbPrimitives = mNbPrimitives - NbPos; + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + Pos->mNodePrimitives = &mNodePrimitives[0]; + Pos->mNbPrimitives = NbPos; + Neg->mNodePrimitives = &mNodePrimitives[NbPos]; + Neg->mNbPrimitives = mNbPrimitives - NbPos; return true; } @@ -296,8 +339,28 @@ void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder) Subdivide(builder); // 3) Recurse - if(mP) mP->_BuildHierarchy(builder); - if(mN) mN->_BuildHierarchy(builder); + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + if(Pos) Pos->_BuildHierarchy(builder); + if(Neg) Neg->_BuildHierarchy(builder); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree (top-down). + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeNode::_Refit(AABBTreeBuilder* builder) +{ + // 1) Recompute the new global box for current node + builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV); + + // 2) Recurse + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + if(Pos) Pos->_Refit(builder); + if(Neg) Neg->_Refit(builder); } @@ -307,7 +370,7 @@ void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder) * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0) +AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0), mPool(null) { } @@ -318,6 +381,17 @@ AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// AABBTree::~AABBTree() { + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the tree. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTree::Release() +{ + DELETEARRAY(mPool); DELETEARRAY(mIndices); } @@ -333,26 +407,42 @@ bool AABBTree::Build(AABBTreeBuilder* builder) // Checkings if(!builder || !builder->mNbPrimitives) return false; + // Release previous tree + Release(); + // Init stats builder->SetCount(1); builder->SetNbInvalidSplits(0); // Initialize indices. This list will be modified during build. - DELETEARRAY(mIndices); - mIndices = new udword[builder->mNbPrimitives]; + mIndices = new udword[builder->mNbPrimitives]; CHECKALLOC(mIndices); + // Identity permutation for(udword i=0;imNbPrimitives;i++) mIndices[i] = i; - // Setup initial box + // Setup initial node. Here we have a complete permutation of the app's primitives. mNodePrimitives = mIndices; mNbPrimitives = builder->mNbPrimitives; + // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3] +// if(builder->mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) + { + // Allocate a pool of nodes + mPool = new AABBTreeNode[builder->mNbPrimitives*2 - 1]; + + builder->mNodeBase = mPool; // ### ugly ! + } + // Build the hierarchy _BuildHierarchy(builder); // Get back total number of nodes mTotalNbNodes = builder->GetCount(); + // For complete trees, check the correct number of nodes has been created [Opcode 1.3] + if(mPool) ASSERT(mTotalNbNodes==builder->mNbPrimitives*2 - 1); + return true; } @@ -365,26 +455,96 @@ bool AABBTree::Build(AABBTreeBuilder* builder) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// udword AABBTree::ComputeDepth() const { - udword Depth = 0; - udword Current = 0; + return Walk(null, null); // Use the walking code without callback +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree, calling the user back for each node. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::Walk(WalkingCallback callback, void* user_data) const +{ + // Call it without callback to compute max depth + udword MaxDepth = 0; + udword CurrentDepth = 0; struct Local { - static void _UpdateDepth(const AABBTreeNode* curnode, udword& depth, udword& current) + static void _Walk(const AABBTreeNode* current_node, udword& max_depth, udword& current_depth, WalkingCallback callback, void* user_data) { // Checkings - if(!curnode) return; + if(!current_node) return; // Entering a new node => increase depth - current++; + current_depth++; // Keep track of max depth - if(current>depth) depth = current; + if(current_depth>max_depth) max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, user_data)) return; + // Recurse - if(curnode->GetPos()) { _UpdateDepth(curnode->GetPos(), depth, current); current--; } - if(curnode->GetNeg()) { _UpdateDepth(curnode->GetNeg(), depth, current); current--; } + if(current_node->GetPos()) { _Walk(current_node->GetPos(), max_depth, current_depth, callback, user_data); current_depth--; } + if(current_node->GetNeg()) { _Walk(current_node->GetNeg(), max_depth, current_depth, callback, user_data); current_depth--; } } }; - Local::_UpdateDepth(this, Depth, Current); - return Depth; + Local::_Walk(this, MaxDepth, CurrentDepth, callback, user_data); + return MaxDepth; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a top-down way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit(AABBTreeBuilder* builder) +{ + if(!builder) return false; + _Refit(builder); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a bottom-up way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit2(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder) return false; + + ASSERT(mPool); + + // Bottom-up update + Point Min,Max; + Point Min_,Max_; + udword Index = mTotalNbNodes; + while(Index--) + { + AABBTreeNode& Current = mPool[Index]; + + if(Current.IsLeaf()) + { + builder->ComputeGlobalBox(Current.GetPrimitives(), Current.GetNbPrimitives(), *(AABB*)Current.GetAABB()); + } + else + { + Current.GetPos()->GetAABB()->GetMin(Min); + Current.GetPos()->GetAABB()->GetMax(Max); + + Current.GetNeg()->GetAABB()->GetMin(Min_); + Current.GetNeg()->GetAABB()->GetMax(Max_); + + Min.Min(Min_); + Max.Max(Max_); + + ((AABB*)Current.GetAABB())->SetMinMax(Min, Max); + } + } + return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/OPC_AABBTree.h b/OPC_AABBTree.h new file mode 100644 index 0000000..377fbcb --- /dev/null +++ b/OPC_AABBTree.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a versatile AABB tree. + * \file OPC_AABBTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_AABBTREE_H__ +#define __OPC_AABBTREE_H__ + +#ifdef OPC_NO_NEG_VANILLA_TREE + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { const base_class* P = GetPos(); return P ? P+1 : null;} \ + \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + udword mPos; /* "Positive" & "Negative" children */ +#else + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { return (const base_class*)(mNeg&~1); } \ + \ +/* inline_ bool IsLeaf() const { return (!GetPos() && !GetNeg()); } */ \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + udword mPos; /* "Positive" child */ \ + udword mNeg; /* "Negative" child */ +#endif + + typedef void (*CullingCallback) (udword nb_primitives, udword* node_primitives, BOOL need_clipping, void* user_data); + + class OPCODE_API AABBTreeNode + { + IMPLEMENT_TREE(AABBTreeNode, AABB) + public: + // Data access + inline_ const udword* GetPrimitives() const { return mNodePrimitives; } + inline_ udword GetNbPrimitives() const { return mNbPrimitives; } + + protected: + // Tree-dependent data + udword* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + udword mNbPrimitives; //!< Number of primitives for this node + // Internal methods + udword Split(udword axis, AABBTreeBuilder* builder); + bool Subdivide(AABBTreeBuilder* builder); + void _BuildHierarchy(AABBTreeBuilder* builder); + void _Refit(AABBTreeBuilder* builder); + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called for each node by the walking code. + * \param current [in] current node + * \param depth [in] current node's depth + * \param user_data [in] user-defined data + * \return true to recurse through children, else false to bypass them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef bool (*WalkingCallback) (const AABBTreeNode* current, udword depth, void* user_data); + + class OPCODE_API AABBTree : public AABBTreeNode + { + public: + // Constructor / Destructor + AABBTree(); + ~AABBTree(); + // Build + bool Build(AABBTreeBuilder* builder); + void Release(); + + // Data access + inline_ const udword* GetIndices() const { return mIndices; } //!< Catch the indices + inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + // Infos + bool IsComplete() const; + // Stats + udword ComputeDepth() const; + udword GetUsedBytes() const; + udword Walk(WalkingCallback callback, void* user_data) const; + + bool Refit(AABBTreeBuilder* builder); + bool Refit2(AABBTreeBuilder* builder); + private: + udword* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + // Stats + udword mTotalNbNodes; //!< Number of nodes in the tree. + }; + +#endif // __OPC_AABBTREE_H__ diff --git a/OpcodeDistrib/OPC_Common.cpp b/OPC_BaseModel.cpp similarity index 51% rename from OpcodeDistrib/OPC_Common.cpp rename to OPC_BaseModel.cpp index 4e75832..4e15809 100644 --- a/OpcodeDistrib/OPC_Common.cpp +++ b/OPC_BaseModel.cpp @@ -8,77 +8,131 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains common classes & defs used in OPCODE. - * \file OPC_Common.cpp + * Contains base model interface. + * \file OPC_BaseModel.cpp * \author Pierre Terdiman - * \date March, 20, 2001 + * \date May, 18, 2003 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * This structure holds 3 vertex-pointers. It's mainly used by collision callbacks so that the app doesn't have - * to return 3 vertices to Opcode (36 bytes) but only 3 pointers (12 bytes). It seems better but I never profiled - * the alternative. + * The base class for collision models. * - * \class VertexPointers + * \class BaseModel * \author Pierre Terdiman - * \version 1.2 - * \date March, 20, 2001 + * \version 1.3 + * \date May, 18, 2003 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * An AABB dedicated to collision detection. - * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends - * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth - * using an extra special class. - * - * \class CollisionAABB - * \author Pierre Terdiman - * \version 1.2 - * \date March, 20, 2001 -*/ + * Constructor. + */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPCODECREATE::OPCODECREATE() +{ + mIMesh = null; + mSettings.mRules = SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER; + mSettings.mLimit = 1; // Mandatory for complete trees + mNoLeaf = true; + mQuantized = true; +#ifdef __MESHMERIZER_H__ + mCollisionHull = false; +#endif // __MESHMERIZER_H__ + mKeepOriginal = false; + mCanRemap = false; +} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A quantized AABB. - * Center/Extent model, using 16-bits integers. - * - * \class QuantizedAABB - * \author Pierre Terdiman - * \version 1.2 - * \date March, 20, 2001 -*/ + * Constructor. + */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::BaseModel() : mIMesh(null), mModelCode(0), mSource(null), mTree(null) +{ +} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * This class describes a face hit by a ray or segment. - * This is a particular class dedicated to stabbing queries. - * - * \class CollisionFace - * \author Pierre Terdiman - * \version 1.2 - * \date March, 20, 2001 -*/ + * Destructor. + */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::~BaseModel() +{ + ReleaseBase(); +} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * This class is a dedicated collection of CollisionFace. - * - * \class CollisionFaces - * \author Pierre Terdiman - * \version 1.2 - * \date March, 20, 2001 -*/ + * Releases everything. + */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void BaseModel::ReleaseBase() +{ + DELETESINGLE(mSource); + DELETESINGLE(mTree); +} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" +/** + * Creates an optimized tree according to user-settings, and setups mModelCode. + * \param no_leaf [in] true for "no leaf" tree + * \param quantized [in] true for quantized tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::CreateTree(bool no_leaf, bool quantized) +{ + DELETESINGLE(mTree); -using namespace Opcode; + // Setup model code + if(no_leaf) mModelCode |= OPC_NO_LEAF; + else mModelCode &= ~OPC_NO_LEAF; + + if(quantized) mModelCode |= OPC_QUANTIZED; + else mModelCode &= ~OPC_QUANTIZED; + + // Create the correct class + if(mModelCode & OPC_NO_LEAF) + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedNoLeafTree; + else mTree = new AABBNoLeafTree; + } + else + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedTree; + else mTree = new AABBCollisionTree; + } + CHECKALLOC(mTree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::Refit() +{ + // Refit the optimized tree + return mTree->Refit(mIMesh); + +// Old code kept for reference : refit the source tree then rebuild ! +// if(!mSource) return false; +// // Ouch... +// mSource->Refit(&mTB); +// // Ouch... +// return mTree->Build(mSource); +} diff --git a/OPC_BaseModel.h b/OPC_BaseModel.h new file mode 100644 index 0000000..15fc423 --- /dev/null +++ b/OPC_BaseModel.h @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base model interface. + * \file OPC_BaseModel.h + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_BASEMODEL_H__ +#define __OPC_BASEMODEL_H__ + + //! Model creation structure + struct OPCODE_API OPCODECREATE + { + //! Constructor + OPCODECREATE(); + + MeshInterface* mIMesh; //!< Mesh interface (access to triangles & vertices) (*) + BuildSettings mSettings; //!< Builder's settings + bool mNoLeaf; //!< true => discard leaf nodes (else use a normal tree) + bool mQuantized; //!< true => quantize the tree (else use a normal tree) +#ifdef __MESHMERIZER_H__ + bool mCollisionHull; //!< true => use convex hull + GJK +#endif // __MESHMERIZER_H__ + bool mKeepOriginal; //!< true => keep a copy of the original tree (debug purpose) + bool mCanRemap; //!< true => allows OPCODE to reorganize client arrays + + // (*) This pointer is saved internally and used by OPCODE until collision structures are released, + // so beware of the object's lifetime. + }; + + enum ModelFlag + { + OPC_QUANTIZED = (1<<0), //!< Compressed/uncompressed tree + OPC_NO_LEAF = (1<<1), //!< Leaf/NoLeaf tree + OPC_SINGLE_NODE = (1<<2) //!< Special case for 1-node models + }; + + class OPCODE_API BaseModel + { + public: + // Constructor/Destructor + BaseModel(); + virtual ~BaseModel(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(const OPCODECREATE& create) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual udword GetUsedBytes() const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the source tree. + * \return generic tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBTree* GetSourceTree() const { return mSource; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBOptimizedTree* GetTree() const { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ AABBOptimizedTree* GetTree() { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of nodes in the tree. + * Should be 2*N-1 for normal trees and N-1 for optimized ones. + * \return number of nodes + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbNodes() const { return mTree->GetNbNodes(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree has leaf nodes or not. + * \return true if the tree has leaf nodes (normal tree), else false (optimized tree) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasLeafNodes() const { return !(mModelCode & OPC_NO_LEAF); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree is quantized or not. + * \return true if the tree is quantized + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsQuantized() const { return mModelCode & OPC_QUANTIZED; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the model has a single node or not. This special case must be handled separately. + * \return true if the model has only 1 node + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasSingleNode() const { return mModelCode & OPC_SINGLE_NODE; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the model's code. + * \return model's code + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetModelCode() const { return mModelCode; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the mesh interface. + * \return mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const MeshInterface* GetMeshInterface() const { return mIMesh; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the mesh interface. + * \param imesh [in] mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMeshInterface(const MeshInterface* imesh) { mIMesh = imesh; } + + protected: + const MeshInterface* mIMesh; //!< User-defined mesh interface + udword mModelCode; //!< Model code = combination of ModelFlag(s) + AABBTree* mSource; //!< Original source tree + AABBOptimizedTree* mTree; //!< Optimized tree owned by the model + // Internal methods + void ReleaseBase(); + bool CreateTree(bool no_leaf, bool quantized); + }; + +#endif //__OPC_BASEMODEL_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_BoxBoxOverlap.h b/OPC_BoxBoxOverlap.h similarity index 100% rename from OpcodeDistrib/OPC_BoxBoxOverlap.h rename to OPC_BoxBoxOverlap.h diff --git a/OPC_BoxPruning.cpp b/OPC_BoxPruning.cpp new file mode 100644 index 0000000..adc2d24 --- /dev/null +++ b/OPC_BoxPruning.cpp @@ -0,0 +1,367 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for box pruning. + * \file IceBoxPruning.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + You could use a complex sweep-and-prune as implemented in I-Collide. + You could use a complex hashing scheme as implemented in V-Clip or recently in ODE it seems. + You could use a "Recursive Dimensional Clustering" algorithm as implemented in GPG2. + + Or you could use this. + Faster ? I don't know. Probably not. It would be a shame. But who knows ? + Easier ? Definitely. Enjoy the sheer simplicity. +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + + inline_ void FindRunningIndex(udword& index, float* array, udword* sorted, int last, float max) + { + int First=index; + while(First<=last) + { + index = (First+last)>>1; + + if(max>array[sorted[index]]) First = index+1; + else last = index-1; + } + } +// ### could be log(n) ! +// and maybe use cmp integers + +// InsertionSort has better coherence, RadixSort is better for one-shot queries. +#define PRUNING_SORTER RadixSort +//#define PRUNING_SORTER InsertionSort + +// Static for coherence +static PRUNING_SORTER* gCompletePruningSorter = null; +static PRUNING_SORTER* gBipartitePruningSorter0 = null; +static PRUNING_SORTER* gBipartitePruningSorter1 = null; +inline_ PRUNING_SORTER* GetCompletePruningSorter() +{ + if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER; + return gCompletePruningSorter; +} +inline_ PRUNING_SORTER* GetBipartitePruningSorter0() +{ + if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER; + return gBipartitePruningSorter0; +} +inline_ PRUNING_SORTER* GetBipartitePruningSorter1() +{ + if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER; + return gBipartitePruningSorter1; +} +void ReleasePruningSorters() +{ + DELETESINGLE(gBipartitePruningSorter1); + DELETESINGLE(gBipartitePruningSorter0); + DELETESINGLE(gCompletePruningSorter); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param array0 [in] array of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param array1 [in] array of boxes for the second set + * \param pairs [out] array of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes) +{ + // Checkings + if(!nb0 || !array0 || !nb1 || !array1) return false; + + // Catch axes + udword Axis0 = axes.mAxis0; + udword Axis1 = axes.mAxis1; + udword Axis2 = axes.mAxis2; + + // Allocate some temporary data + float* MinPosList0 = new float[nb0]; + float* MinPosList1 = new float[nb1]; + + // 1) Build main lists using the primary axis + for(udword i=0;iGetMin(Axis0); + for(udword i=0;iGetMin(Axis0); + + // 2) Sort the lists + PRUNING_SORTER* RS0 = GetBipartitePruningSorter0(); + PRUNING_SORTER* RS1 = GetBipartitePruningSorter1(); + const udword* Sorted0 = RS0->Sort(MinPosList0, nb0).GetRanks(); + const udword* Sorted1 = RS1->Sort(MinPosList1, nb1).GetRanks(); + + // 3) Prune the lists + udword Index0, Index1; + + const udword* const LastSorted0 = &Sorted0[nb0]; + const udword* const LastSorted1 = &Sorted1[nb1]; + const udword* RunningAddress0 = Sorted0; + const udword* RunningAddress1 = Sorted1; + + while(RunningAddress1GetMax(Axis0)) + { + if(array0[Index0]->Intersect(*array1[Index1], Axis1)) + { + if(array0[Index0]->Intersect(*array1[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } + } + } + + //// + + while(RunningAddress0GetMax(Axis0)) + { + if(array0[Index1]->Intersect(*array1[Index0], Axis1)) + { + if(array0[Index1]->Intersect(*array1[Index0], Axis2)) + { + pairs.AddPair(Index1, Index0); + } + } + + } + } + + DELETEARRAY(MinPosList1); + DELETEARRAY(MinPosList0); + + return true; +} + +#define ORIGINAL_VERSION +//#define JOAKIM + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. + * \param nb [in] number of boxes + * \param array [in] array of boxes + * \param pairs [out] array of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes) +{ + // Checkings + if(!nb || !array) return false; + + // Catch axes + udword Axis0 = axes.mAxis0; + udword Axis1 = axes.mAxis1; + udword Axis2 = axes.mAxis2; + +#ifdef ORIGINAL_VERSION + // Allocate some temporary data +// float* PosList = new float[nb]; + float* PosList = new float[nb+1]; + + // 1) Build main list using the primary axis + for(udword i=0;iGetMin(Axis0); +PosList[nb++] = MAX_FLOAT; + + // 2) Sort the list + PRUNING_SORTER* RS = GetCompletePruningSorter(); + const udword* Sorted = RS->Sort(PosList, nb).GetRanks(); + + // 3) Prune the list + const udword* const LastSorted = &Sorted[nb]; + const udword* RunningAddress = Sorted; + udword Index0, Index1; + while(RunningAddressGetMax(Axis0)) + while(PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0)) + { +// if(Index0!=Index1) +// { + if(array[Index0]->Intersect(*array[Index1], Axis1)) + { + if(array[Index0]->Intersect(*array[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } +// } + } + } + } + + DELETEARRAY(PosList); +#endif + +#ifdef JOAKIM + // Allocate some temporary data +// float* PosList = new float[nb]; + float* MinList = new float[nb+1]; + + // 1) Build main list using the primary axis + for(udword i=0;iGetMin(Axis0); + MinList[nb] = MAX_FLOAT; + + // 2) Sort the list + PRUNING_SORTER* RS = GetCompletePruningSorter(); + udword* Sorted = RS->Sort(MinList, nb+1).GetRanks(); + + // 3) Prune the list +// const udword* const LastSorted = &Sorted[nb]; +// const udword* const LastSorted = &Sorted[nb-1]; + const udword* RunningAddress = Sorted; + udword Index0, Index1; + +// while(RunningAddressGetMax(Axis0)) + +// float CurrentMin = array[Index0]->GetMin(Axis0); + float CurrentMax = array[Index0]->GetMax(Axis0); + + while(MinList[Index1 = *RunningAddress2] <= CurrentMax) +// while(PosList[Index1 = *RunningAddress] <= CurrentMax) + { +// if(Index0!=Index1) +// { + if(array[Index0]->Intersect(*array[Index1], Axis1)) + { + if(array[Index0]->Intersect(*array[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } +// } + + RunningAddress2++; +// RunningAddress++; + } + } + } + + DELETEARRAY(MinList); +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Brute-force versions are kept: +// - to check the optimized versions return the correct list of intersections +// - to check the speed of the optimized code against the brute-force one +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Brute-force bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param array0 [in] array of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param array1 [in] array of boxes for the second set + * \param pairs [out] array of overlapping pairs + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs) +{ + // Checkings + if(!nb0 || !array0 || !nb1 || !array1) return false; + + // Brute-force nb0*nb1 overlap tests + for(udword i=0;iIntersect(*array1[j])) pairs.AddPair(i, j); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. + * \param nb [in] number of boxes + * \param array [in] array of boxes + * \param pairs [out] array of overlapping pairs + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs) +{ + // Checkings + if(!nb || !array) return false; + + // Brute-force n(n-1)/2 overlap tests + for(udword i=0;iIntersect(*array[j])) pairs.AddPair(i, j); + } + } + return true; +} diff --git a/OPC_BoxPruning.h b/OPC_BoxPruning.h new file mode 100644 index 0000000..ef65c80 --- /dev/null +++ b/OPC_BoxPruning.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for box pruning. + * \file IceBoxPruning.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_BOXPRUNING_H__ +#define __OPC_BOXPRUNING_H__ + + // Optimized versions + FUNCTION OPCODE_API bool CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes); + FUNCTION OPCODE_API bool BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes); + + // Brute-force versions + FUNCTION OPCODE_API bool BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs); + FUNCTION OPCODE_API bool BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs); + +#endif //__OPC_BOXPRUNING_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_Collider.cpp b/OPC_Collider.cpp similarity index 94% rename from OpcodeDistrib/OPC_Collider.cpp rename to OPC_Collider.cpp index 6f0340d..bb9663d 100644 --- a/OpcodeDistrib/OPC_Collider.cpp +++ b/OPC_Collider.cpp @@ -21,7 +21,7 @@ * * \class Collider * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date June, 2, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -37,7 +37,10 @@ using namespace Opcode; * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Collider::Collider() : mFlags(0) +Collider::Collider() : + mFlags (0), + mCurrentModel (null), + mIMesh (null) { } diff --git a/OpcodeDistrib/OPC_Collider.h b/OPC_Collider.h similarity index 71% rename from OpcodeDistrib/OPC_Collider.h rename to OPC_Collider.h index a3709d8..4495093 100644 --- a/OpcodeDistrib/OPC_Collider.h +++ b/OPC_Collider.h @@ -26,6 +26,7 @@ OPC_TEMPORAL_COHERENCE = (1<<1), //!< Use temporal coherence or not OPC_CONTACT = (1<<2), //!< Final contact status after a collision query OPC_TEMPORAL_HIT = (1<<3), //!< There has been an early exit due to temporal coherence + OPC_NO_PRIMITIVE_TESTS = (1<<4), //!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries) OPC_CONTACT_FOUND = OPC_FIRST_CONTACT | OPC_CONTACT, OPC_TEMPORAL_CONTACT = OPC_TEMPORAL_HIT | OPC_CONTACT, @@ -82,6 +83,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ BOOL TemporalHit() const { return mFlags & OPC_TEMPORAL_HIT; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks primitive tests are enabled; + * \return true if primitive tests must be skipped + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL SkipPrimitiveTests() const { return mFlags & OPC_NO_PRIMITIVE_TESTS; } + // Settings /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -93,10 +102,10 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void SetFirstContact(bool flag) - { - if(flag) mFlags |= OPC_FIRST_CONTACT; - else mFlags &= ~OPC_FIRST_CONTACT; - } + { + if(flag) mFlags |= OPC_FIRST_CONTACT; + else mFlags &= ~OPC_FIRST_CONTACT; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -107,10 +116,22 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void SetTemporalCoherence(bool flag) - { - if(flag) mFlags |= OPC_TEMPORAL_COHERENCE; - else mFlags &= ~OPC_TEMPORAL_COHERENCE; - } + { + if(flag) mFlags |= OPC_TEMPORAL_COHERENCE; + else mFlags &= ~OPC_TEMPORAL_COHERENCE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable primitive tests. + * \param flag [in] true to enable primitive tests, false to discard them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetPrimitiveTests(bool flag) + { + if(!flag) mFlags |= OPC_NO_PRIMITIVE_TESTS; + else mFlags &= ~OPC_NO_PRIMITIVE_TESTS; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -118,14 +139,31 @@ * \return null if everything is ok, else a string describing the problem */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - virtual const char* ValidateSettings() = 0 - { - return "Collider::ValidateSettings: pure virtual function called!"; - } + virtual const char* ValidateSettings() = 0; + protected: + udword mFlags; //!< Bit flags + const BaseModel* mCurrentModel; //!< Current model for collision query (owner of touched faces) + // User mesh interface + const MeshInterface* mIMesh; //!< User-defined mesh interface - udword mFlags; //!< Bit flags // Internal methods + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups current collision model + * \param model [in] current collision model + * \return TRUE if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Setup(const BaseModel* model) + { + // Keep track of current model + mCurrentModel = model; + if(!mCurrentModel) return FALSE; + + mIMesh = model->GetMeshInterface(); + return mIMesh!=null; + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** diff --git a/OpcodeDistrib/OPC_AABB.cpp b/OPC_Common.cpp similarity index 64% rename from OpcodeDistrib/OPC_AABB.cpp rename to OPC_Common.cpp index 8b0ef7e..839186b 100644 --- a/OpcodeDistrib/OPC_AABB.cpp +++ b/OPC_Common.cpp @@ -1,66 +1,48 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains AABB-related code. - * \file IceAABB.cpp - * \author Pierre Terdiman - * \date January, 29, 2000 +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * AABB class. - * \class AABB + * Contains common classes & defs used in OPCODE. + * \file OPC_Common.cpp * \author Pierre Terdiman - * \version 1.0 + * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -using namespace Meshmerizer; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Computes the sum of two AABBs. - * \param aabb [in] the other AABB - * \return Self-Reference - */ + * An AABB dedicated to collision detection. + * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends + * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth + * using an extra special class. + * + * \class CollisionAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -AABB& AABB::Add(const AABB& aabb) -{ - // Compute new min & max values - Point Min; GetMin(Min); - Point Tmp; aabb.GetMin(Tmp); - Min.Min(Tmp); - - Point Max; GetMax(Max); - aabb.GetMax(Tmp); - Max.Max(Tmp); - - // Update this - SetMinMax(Min, Max); - return *this; -} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Checks a box is inside another box. - * \param box [in] the other AABB - * \return true if current box is inside input box - */ + * A quantized AABB. + * Center/Extent model, using 16-bits integers. + * + * \class QuantizedAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool AABB::IsInside(const AABB& box) const -{ - // ### to customize - if(box.GetMin(0)>GetMin(0)) return false; - if(box.GetMin(1)>GetMin(1)) return false; - if(box.GetMin(2)>GetMin(2)) return false; - if(box.GetMax(0) (y) #endif - struct VertexPointers - { - const Point* Vertex[3]; - }; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * User-callback, called by OPCODE to request vertices from the app. - * \param triangle_index [in] face index for which the system is requesting the vertices - * \param triangle [out] triangle's vertices (must be provided by the user) - * \param user_data [in] user-defined data from SetCallback() - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - typedef void (*OPC_CALLBACK) (udword triangle_index, VertexPointers& triangle, udword user_data); - class OPCODE_API CollisionAABB { public: @@ -52,11 +37,25 @@ //! Destructor inline_ ~CollisionAABB() {} + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + //! Get component of the box's min point along a given axis inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } //! Get component of the box's max point along a given axis inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Checks a box is inside another box. @@ -64,15 +63,15 @@ * \return true if current box is inside input box */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool IsInside(const CollisionAABB& box) const + inline_ BOOL IsInside(const CollisionAABB& box) const { - if(box.GetMin(0)>GetMin(0)) return false; - if(box.GetMin(1)>GetMin(1)) return false; - if(box.GetMin(2)>GetMin(2)) return false; - if(box.GetMax(0)GetMin(0)) return FALSE; + if(box.GetMin(1)>GetMin(1)) return FALSE; + if(box.GetMin(2)>GetMin(2)) return FALSE; + if(box.GetMax(0)>2; } - inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); } - - inline_ void Reset() { Container::Reset(); } - - inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); } - }; - //! Quickly rotates & translates a vector inline_ void TransformPoint(Point& dest, const Point& source, const Matrix3x3& rot, const Point& trans) { diff --git a/OPC_HybridModel.cpp b/OPC_HybridModel.cpp new file mode 100644 index 0000000..f922f6d --- /dev/null +++ b/OPC_HybridModel.cpp @@ -0,0 +1,466 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for hybrid models. + * \file OPC_HybridModel.cpp + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An hybrid collision model. + * + * The problem : + * + * Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping + * (it typically outperforms RAPID in those cases). + * + * Unfortunately this is not the typical scenario in games. + * + * For close-proximity cases, especially for volume-mesh queries, it's relatively easy to run faster + * than Opcode, that suffers from a relatively high setup time. + * + * In particular, Opcode's "vanilla" trees in those cases -can- run faster. They can also use -less- + * memory than the optimized ones, when you let the system stop at ~10 triangles / leaf for example + * (i.e. when you don't use "complete" trees). However, those trees tend to fragment memory quite a + * lot, increasing cache misses : since they're not "complete", we can't predict the final number of + * nodes and we have to allocate nodes on-the-fly. For the same reasons we can't use Opcode's "optimized" + * trees here, since they rely on a known layout to perform the "optimization". + * + * Hybrid trees : + * + * Hybrid trees try to combine best of both worlds : + * + * - they use a maximum limit of 16 triangles/leaf. "16" is used so that we'll be able to save the + * number of triangles using 4 bits only. + * + * - they're still "complete" trees thanks to a two-passes building phase. First we create a "vanilla" + * AABB-tree with Opcode, limited to 16 triangles/leaf. Then we create a *second* vanilla tree, this + * time using the leaves of the first one. The trick is : this second tree is now "complete"... so we + * can further transform it into an Opcode's optimized tree. + * + * - then we run the collision queries on that standard Opcode tree. The only difference is that leaf + * nodes contain indices to leaf nodes of another tree. Also, we have to skip all primitive tests in + * Opcode optimized trees, since our leaves don't contain triangles anymore. + * + * - finally, for each collided leaf, we simply loop through 16 triangles max, and collide them with + * the bounding volume used in the query (we only support volume-vs-mesh queries here, not mesh-vs-mesh) + * + * All of that is wrapped in this "hybrid model" that contains the minimal data required for this to work. + * It's a mix between old "vanilla" trees, and old "optimized" trees. + * + * Extra advantages: + * + * - If we use them for dynamic models, we're left with a very small number of leaf nodes to refit. It + * might be a bit faster since we have less nodes to write back. + * + * - In rigid body simulation, using temporal coherence and sleeping objects greatly reduce the actual + * influence of one tree over another (i.e. the speed difference is often invisible). So memory is really + * the key element to consider, and in this regard hybrid trees are just better. + * + * Information to take home: + * - they use less ram + * - they're not slower (they're faster or slower depending on cases, overall there's no significant + * difference *as long as objects don't interpenetrate too much* - in which case Opcode's optimized trees + * are still notably faster) + * + * \class HybridModel + * \author Pierre Terdiman + * \version 1.3 + * \date May, 18, 2003 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridModel::HybridModel() : + mNbLeaves (0), + mNbPrimitives (0), + mTriangles (null), + mIndices (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridModel::~HybridModel() +{ + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases everything. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void HybridModel::Release() +{ + ReleaseBase(); + DELETEARRAY(mIndices); + DELETEARRAY(mTriangles); + mNbLeaves = 0; + mNbPrimitives = 0; +} + + struct Internal + { + Internal() + { + mNbLeaves = 0; + mLeaves = null; + mTriangles = null; + mBase = null; + } + ~Internal() + { + DELETEARRAY(mLeaves); + } + + udword mNbLeaves; + AABB* mLeaves; + LeafTriangles* mTriangles; + const udword* mBase; + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool HybridModel::Build(const OPCODECREATE& create) +{ + // 1) Checkings + if(!create.mIMesh || !create.mIMesh->IsValid()) return false; + + // Look for degenerate faces. + udword NbDegenerate = create.mIMesh->CheckTopology(); + if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); + // We continue nonetheless.... + + Release(); // Make sure previous tree has been discarded + + // 1-1) Setup mesh interface automatically + SetMeshInterface(create.mIMesh); + + bool Status = false; + AABBTree* LeafTree = null; + Internal Data; + + // 2) Build a generic AABB Tree. + mSource = new AABBTree; + CHECKALLOC(mSource); + + // 2-1) Setup a builder. Our primitives here are triangles from input mesh, + // so we use an AABBTreeOfTrianglesBuilder..... + { + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mNbPrimitives = create.mIMesh->GetNbTriangles(); + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ... + if(!mSource->Build(&TB)) goto FreeAndExit; + } + + // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time) + struct Local + { + // A callback to count leaf nodes + static bool CountLeaves(const AABBTreeNode* current, udword depth, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + Data->mNbLeaves++; + } + return true; + } + + // A callback to setup leaf nodes in our internal structures + static bool SetupLeafData(const AABBTreeNode* current, udword depth, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + + // Get current leaf's box + Data->mLeaves[Data->mNbLeaves] = *current->GetAABB(); + + // Setup leaf data + udword Index = (udword(current->GetPrimitives()) - udword(Data->mBase))/sizeof(udword); + Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index); + + Data->mNbLeaves++; + } + return true; + } + }; + + // Walk the tree & count number of leaves + Data.mNbLeaves = 0; + mSource->Walk(Local::CountLeaves, &Data); + mNbLeaves = Data.mNbLeaves; // Keep track of it + + // Special case for 1-leaf meshes + if(mNbLeaves==1) + { + mModelCode |= OPC_SINGLE_NODE; + Status = true; + goto FreeAndExit; + } + + // Allocate our structures + Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves); + mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles); + + // Walk the tree again & setup leaf data + Data.mTriangles = mTriangles; + Data.mBase = mSource->GetIndices(); + Data.mNbLeaves = 0; // Reset for incoming walk + mSource->Walk(Local::SetupLeafData, &Data); + + // Handle source indices + { + bool MustKeepIndices = true; + if(create.mCanRemap) + { + // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays... + // Remap can fail when we use callbacks => keep track of indices in that case (it still + // works, only using more memory) + if(create.mIMesh->RemapClient(mSource->GetNbPrimitives(), mSource->GetIndices())) + { + MustKeepIndices = false; + } + } + + if(MustKeepIndices) + { + // Keep track of source indices (from vanilla tree) + mNbPrimitives = mSource->GetNbPrimitives(); + mIndices = new udword[mNbPrimitives]; + CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword)); + } + } + + // Now, create our optimized tree using previous leaf nodes + LeafTree = new AABBTree; + CHECKALLOC(LeafTree); + { + AABBTreeOfAABBsBuilder TB; // Now using boxes ! + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it + TB.mNbPrimitives = Data.mNbLeaves; + TB.mAABBArray = Data.mLeaves; + if(!LeafTree->Build(&TB)) goto FreeAndExit; + } + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit; + + // 3-2) Create optimized tree + if(!mTree->Build(LeafTree)) goto FreeAndExit; + + // Finally ok... + Status = true; + +FreeAndExit: // Allow me this one... + DELETESINGLE(LeafTree); + + // 3-3) Delete generic tree if needed + if(!create.mKeepOriginal) DELETESINGLE(mSource); + + return Status; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword HybridModel::GetUsedBytes() const +{ + udword UsedBytes = 0; + if(mTree) UsedBytes += mTree->GetUsedBytes(); + if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices + if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles + return UsedBytes; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool HybridModel::Refit() +{ + if(!mIMesh) return false; + if(!mTree) return false; + + if(IsQuantized()) return false; + if(HasLeafNodes()) return false; + + const LeafTriangles* LT = GetLeafTriangles(); + const udword* Indices = GetIndices(); + + // Bottom-up update + VertexPointers VP; + Point Min,Max; + Point Min_,Max_; + udword Index = mTree->GetNbNodes(); + AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes(); + while(Index--) + { + AABBNoLeafNode& Current = Nodes[Index]; + + if(Current.HasPosLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()]; + + Min.SetPlusInfinity(); + Max.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + + if(Current.HasNegLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()]; + + Min_.SetPlusInfinity(); + Max_.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} diff --git a/OpcodeDistrib/OPC_Model.h b/OPC_HybridModel.h similarity index 58% rename from OpcodeDistrib/OPC_Model.h rename to OPC_HybridModel.h index eb2af9b..7833a94 100644 --- a/OpcodeDistrib/OPC_Model.h +++ b/OPC_HybridModel.h @@ -8,119 +8,99 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains code for OPCODE models. - * \file OPC_Model.h + * Contains code for hybrid models. + * \file OPC_HybridModel.h * \author Pierre Terdiman - * \date March, 20, 2001 + * \date May, 18, 2003 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __OPC_MODEL_H__ -#define __OPC_MODEL_H__ +#ifndef __OPC_HYBRIDMODEL_H__ +#define __OPC_HYBRIDMODEL_H__ - //! Model creation structure - struct OPCODE_API OPCODECREATE + //! Leaf descriptor + struct LeafTriangles { - //! Constructor - OPCODECREATE(); - - udword NbTris; //!< Number of triangles in the input model - udword NbVerts; //!< Number of vertices in the input model - const udword* Tris; //!< List of indexed triangles - const Point* Verts; //!< List of points - udword Rules; //!< Splitting rules (SPLIT_COMPLETE is mandatory in OPCODE) - bool NoLeaf; //!< true => discard leaf nodes (else use a normal tree) - bool Quantized; //!< true => quantize the tree (else use a normal tree) -#ifdef __MESHMERIZER_H__ - bool CollisionHull; //!< true => use convex hull + GJK -#endif // __MESHMERIZER_H__ - bool KeepOriginal; //!< true => keep a copy of the original tree (debug purpose) - }; - - class OPCODE_API OPCODE_Model - { - public: - // Constructor/Destructor - OPCODE_Model(); - ~OPCODE_Model(); + udword Data; //!< Packed data /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Builds a collision model. - * \param create [in] model creation structure - * \return true if success + * Gets number of triangles in the leaf. + * \return number of triangles N, with 0 < N <= 16 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Build(const OPCODECREATE& create); + inline_ udword GetNbTriangles() const { return (Data & 15)+1; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to access the tree. - * \return the collision tree + * Gets triangle index for this leaf. Indexed model's array of indices retrieved with HybridModel::GetIndices() + * \return triangle index */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ const AABBOptimizedTree* GetTree() const { return mTree; } + inline_ udword GetTriangleIndex() const { return Data>>4; } + inline_ void SetData(udword nb, udword index) { ASSERT(nb>0 && nb<=16); nb--; Data = (index<<4)|(nb&15); } + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * A method to access the source tree. - * \return generic tree - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ const AABBTree* GetSourceTree() const { return mSource; } + class OPCODE_API HybridModel : public BaseModel + { + public: + // Constructor/Destructor + HybridModel(); + virtual ~HybridModel(); -#ifdef __MESHMERIZER_H__ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to access the collision hull. - * \return the collision hull if it exists + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ const CollisionHull* GetHull() const { return mHull; } -#endif // __MESHMERIZER_H__ + override(BaseModel) bool Build(const OPCODECREATE& create); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Checks whether the tree has leaf nodes or not. - * \return true if the tree has leaf nodes (normal tree), else false (optimized tree) + * Gets the number of bytes used by the tree. + * \return amount of bytes used */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool HasLeafNodes() const { return !mNoLeaf; } + override(BaseModel) udword GetUsedBytes() const; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Checks whether the tree is quantized or not. - * \return true if the tree is quantized + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ bool IsQuantized() const { return mQuantized; } + override(BaseModel) bool Refit(); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Gets the number of nodes in the tree. - * Should be 2*N-1 for normal trees and N-1 for optimized ones. - * \return number of nodes + * Gets array of triangles. + * \return array of triangles */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - udword GetNbNodes() const { return mTree->GetNbNodes(); } + inline_ const LeafTriangles* GetLeafTriangles() const { return mTriangles; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Gets the number of bytes used by the tree. - * \return amount of bytes used + * Gets array of indices. + * \return array of indices */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - udword GetUsedBytes() const { return mTree->GetUsedBytes(); } + inline_ const udword* GetIndices() const { return mIndices; } + private: - AABBTree* mSource; //!< Original source tree - AABBOptimizedTree* mTree; //!< Optimized tree -#ifdef __MESHMERIZER_H__ - CollisionHull* mHull; //!< Possible convex hull -#endif // __MESHMERIZER_H__ - bool mNoLeaf; //!< Leaf/NoLeaf tree - bool mQuantized; //!< Compressed/uncompressed tree + udword mNbLeaves; //!< Number of leaf nodes in the model + LeafTriangles* mTriangles; //!< Array of mNbLeaves leaf descriptors + udword mNbPrimitives; //!< Number of primitives in the model + udword* mIndices; //!< Array of primitive indices + + // Internal methods + void Release(); }; -#endif //__OPC_MODEL_H__ \ No newline at end of file +#endif // __OPC_HYBRIDMODEL_H__ diff --git a/OPC_IceHook.h b/OPC_IceHook.h new file mode 100644 index 0000000..8b97eaa --- /dev/null +++ b/OPC_IceHook.h @@ -0,0 +1,70 @@ + +// Should be included by Opcode.h if needed + + #define ICE_DONT_CHECK_COMPILER_OPTIONS + + // From Windows... + typedef int BOOL; + #ifndef FALSE + #define FALSE 0 + #endif + + #ifndef TRUE + #define TRUE 1 + #endif + + #include + #include + #include + #include + #include + #include + + #ifndef ASSERT + #define ASSERT(exp) {} + #endif + #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ] + + #define Log {} + #define SetIceError false + #define EC_OUTOFMEMORY "Out of memory" + + #include ".\Ice\IcePreprocessor.h" + + #undef ICECORE_API + #define ICECORE_API OPCODE_API + + #include ".\Ice\IceTypes.h" + #include ".\Ice\IceFPU.h" + #include ".\Ice\IceMemoryMacros.h" + + namespace IceCore + { + #include ".\Ice\IceUtils.h" + #include ".\Ice\IceContainer.h" + #include ".\Ice\IcePairs.h" + #include ".\Ice\IceRevisitedRadix.h" + #include ".\Ice\IceRandom.h" + } + using namespace IceCore; + + #define ICEMATHS_API OPCODE_API + namespace IceMaths + { + #include ".\Ice\IceAxes.h" + #include ".\Ice\IcePoint.h" + #include ".\Ice\IceHPoint.h" + #include ".\Ice\IceMatrix3x3.h" + #include ".\Ice\IceMatrix4x4.h" + #include ".\Ice\IcePlane.h" + #include ".\Ice\IceRay.h" + #include ".\Ice\IceIndexedTriangle.h" + #include ".\Ice\IceTriangle.h" + #include ".\Ice\IceTriList.h" + #include ".\Ice\IceAABB.h" + #include ".\Ice\IceOBB.h" + #include ".\Ice\IceBoundingSphere.h" + #include ".\Ice\IceSegment.h" + #include ".\Ice\IceLSS.h" + } + using namespace IceMaths; diff --git a/OPC_LSSAABBOverlap.h b/OPC_LSSAABBOverlap.h new file mode 100644 index 0000000..c36b250 --- /dev/null +++ b/OPC_LSSAABBOverlap.h @@ -0,0 +1,523 @@ + +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +inline_ float OPC_PointAABBSqrDist(const Point& point, const Point& center, const Point& extents) +{ + // Compute coordinates of point in box coordinate system + Point Closest = point - center; + + float SqrDistance = 0.0f; + + if(Closest.x < -extents.x) + { + float Delta = Closest.x + extents.x; + SqrDistance += Delta*Delta; + } + else if(Closest.x > extents.x) + { + float Delta = Closest.x - extents.x; + SqrDistance += Delta*Delta; + } + + if(Closest.y < -extents.y) + { + float Delta = Closest.y + extents.y; + SqrDistance += Delta*Delta; + } + else if(Closest.y > extents.y) + { + float Delta = Closest.y - extents.y; + SqrDistance += Delta*Delta; + } + + if(Closest.z < -extents.z) + { + float Delta = Closest.z + extents.z; + SqrDistance += Delta*Delta; + } + else if(Closest.z > extents.z) + { + float Delta = Closest.z - extents.z; + SqrDistance += Delta*Delta; + } + return SqrDistance; +} + +static void Face(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, const Point& rkPmE, float* pfLParam, float& rfSqrDistance) +{ + Point kPpE; + float fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void CaseNoZeros(Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + Point kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + float fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + Face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + Face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void Case0(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fPmE0 = rkPnt[i0] - extents[i0]; + float fPmE1 = rkPnt[i1] - extents[i1]; + float fProd0 = rkDir[i1]*fPmE0; + float fProd1 = rkDir[i0]*fPmE1; + float fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + float fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + float fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case00(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case000(Point& rkPnt, const Point& extents, float& rfSqrDistance) +{ + float fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +static float SqrDistance(const Ray& rkLine, const Point& center, const Point& extents, float* pfLParam) +{ + // compute coordinates of line in box coordinate system + Point kDiff = rkLine.mOrig - center; + Point kPnt = kDiff; + Point kDir = rkLine.mDir; + + // Apply reflections so that direction vector has nonnegative components. + bool bReflect[3]; + for(int i=0;i<3;i++) + { + if(kDir[i]<0.0f) + { + kPnt[i] = -kPnt[i]; + kDir[i] = -kDir[i]; + bReflect[i] = true; + } + else + { + bReflect[i] = false; + } + } + + float fSqrDistance = 0.0f; + + if(kDir.x>0.0f) + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) CaseNoZeros(kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,+) + else Case0(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,0) + } + else + { + if(kDir.z>0.0f) Case0(0, 2, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,+) + else Case00(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,0) + } + } + else + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) Case0(1, 2, 0, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,+) + else Case00(1, 0, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,0) + } + else + { + if(kDir.z>0.0f) Case00(2, 0, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,0,+) + else + { + Case000(kPnt, extents, fSqrDistance); // (0,0,0) + if(pfLParam) *pfLParam = 0.0f; + } + } + } + return fSqrDistance; +} + +inline_ float OPC_SegmentOBBSqrDist(const Segment& segment, const Point& c0, const Point& e0) +{ + float fLP; + float fSqrDistance = SqrDistance(Ray(segment.GetOrigin(), segment.ComputeDirection()), c0, e0, &fLP); + if(fLP>=0.0f) + { + if(fLP<=1.0f) return fSqrDistance; + else return OPC_PointAABBSqrDist(segment.mP1, c0, e0); + } + else return OPC_PointAABBSqrDist(segment.mP0, c0, e0); +} + +inline_ BOOL LSSCollider::LSSAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbVolumeBVTests++; + + float s2 = OPC_SegmentOBBSqrDist(mSeg, center, extents); + if(s2Add(prim_index); + +//! LSS-triangle overlap test +#define LSS_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform LSS-tri overlap test */ \ + if(LSSTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::LSSCollider() +{ +// mCenter.Zero(); +// mRadius2 = 0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::~LSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] an lss cache + * \param lss [in] lss in local space + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL LSSCollider::InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute LSS in model space: + // - Precompute R^2 + mRadius2 = lss.mRadius * lss.mRadius; + // - Compute segment + mSeg.mP0 = lss.mP0; + mSeg.mP1 = lss.mP1; + // -> to world space + if(worldl) + { + mSeg.mP0 *= *worldl; + mSeg.mP1 *= *worldl; + } + // -> to model space + if(worldm) + { + // Invert model matrix + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + mSeg.mP0 *= InvWorldM; + mSeg.mP1 *= InvWorldM; + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the LSS (and set contact status if needed) + LSS_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the LSS (and set contact status if needed) + LSS_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real LSS N(ew) against the previous fat LSS P(revious): + + // ### rewrite this + + LSS Test(mSeg, lss.mRadius); // in model space + LSS Previous(cache.Previous, sqrtf(cache.Previous.mRadius)); + +// if(cache.Previous.Contains(Test)) + if(IsCacheValid(cache) && Previous.Contains(Test)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat sphere so that coherence will work for subsequent frames + mRadius2 *= cache.FatCoeff; +// mRadius2 = (lss.mRadius * cache.FatCoeff)*(lss.mRadius * cache.FatCoeff); + + + // Update cache with query data (signature for cached faces) + cache.Previous.mP0 = mSeg.mP0; + cache.Previous.mP1 = mSeg.mP1; + cache.Previous.mRadius = mRadius2; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, lss)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the LSS completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the LSS contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL LSSCollider::LSSContainsBox(const Point& bc, const Point& be) +{ + // Not implemented + return FALSE; +} + +#define TEST_BOX_IN_LSS(center, extents) \ + if(LSSContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBTreeNode* node) +{ + // Perform LSS-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!LSSAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || LSSContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::HybridLSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::~HybridLSSCollider() +{ +} + +bool HybridLSSCollider::Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/OPC_LSSCollider.h b/OPC_LSSCollider.h new file mode 100644 index 0000000..7b69448 --- /dev/null +++ b/OPC_LSSCollider.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an LSS collider. + * \file OPC_LSSCollider.h + * \author Pierre Terdiman + * \date December, 28, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_LSSCOLLIDER_H__ +#define __OPC_LSSCOLLIDER_H__ + + struct OPCODE_API LSSCache : VolumeCache + { + LSSCache() + { + Previous.mP0 = Point(0.0f, 0.0f, 0.0f); + Previous.mP1 = Point(0.0f, 0.0f, 0.0f); + Previous.mRadius = 0.0f; + FatCoeff = 1.1f; + } + + // Cached faces signature + LSS Previous; //!< LSS used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat LSS + }; + + class OPCODE_API LSSCollider : public VolumeCollider + { + public: + // Constructor / Destructor + LSSCollider(); + virtual ~LSSCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + // + bool Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree); + protected: + // LSS in model space + Segment mSeg; //!< Segment + float mRadius2; //!< LSS radius squared + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL LSSContainsBox(const Point& bc, const Point& be); + inline_ BOOL LSSAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridLSSCollider : public LSSCollider + { + public: + // Constructor / Destructor + HybridLSSCollider(); + virtual ~HybridLSSCollider(); + + bool Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + +#endif // __OPC_LSSCOLLIDER_H__ diff --git a/OPC_LSSTriOverlap.h b/OPC_LSSTriOverlap.h new file mode 100644 index 0000000..728ef87 --- /dev/null +++ b/OPC_LSSTriOverlap.h @@ -0,0 +1,679 @@ +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +static const float gs_fTolerance = 1e-05f; + +static float OPC_PointTriangleSqrDist(const Point& point, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + Point TriEdge0 = p1 - p0; + Point TriEdge1 = p2 - p0; + + Point kDiff = p0 - point; + float fA00 = TriEdge0.SquareMagnitude(); + float fA01 = TriEdge0 | TriEdge1; + float fA11 = TriEdge1.SquareMagnitude(); + float fB0 = kDiff | TriEdge0; + float fB1 = kDiff | TriEdge1; + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11 - fA01*fA01); + float fS = fA01*fB1-fA11*fB0; + float fT = fA01*fB0-fA00*fB1; + float fSqrDist; + + if(fS + fT <= fDet) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else // region 3 + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 5 + { + if(fB0 >= 0.0f) fSqrDist = fC; + else if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { + fSqrDist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(fS < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(fB1 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = fNumer/fDenom; + fS = 1.0f - fT; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(fB0 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + } + return fabsf(fSqrDist); +} + +static float OPC_SegmentSegmentSqrDist(const Segment& rkSeg0, const Segment& rkSeg1) +{ + // Hook + Point rkSeg0Direction = rkSeg0.ComputeDirection(); + Point rkSeg1Direction = rkSeg1.ComputeDirection(); + + Point kDiff = rkSeg0.mP0 - rkSeg1.mP0; + float fA00 = rkSeg0Direction.SquareMagnitude(); + float fA01 = -rkSeg0Direction.Dot(rkSeg1Direction); + float fA11 = rkSeg1Direction.SquareMagnitude(); + float fB0 = kDiff.Dot(rkSeg0Direction); + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11-fA01*fA01); + + float fB1, fS, fT, fSqrDist, fTmp; + + if(fDet>=gs_fTolerance) + { + // line segments are not parallel + fB1 = -kDiff.Dot(rkSeg1Direction); + fS = fA01*fB1-fA11*fB0; + fT = fA01*fB0-fA00*fB1; + + if(fS >= 0.0f) + { + if(fS <= fDet) + { + if(fT >= 0.0f) + { + if(fT <= fDet) // region 0 (interior) + { + // minimum at two interior points of 3D lines + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fTmp = fA01+fB0; + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + } + else // region 7 (side) + { + if(fB0>=0.0f) fSqrDist = fC; + else if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else + { + if ( fT >= 0.0 ) + { + if ( fT <= fDet ) // region 1 (side) + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + else // region 2 (corner) + { + fTmp = fA01+fB0; + if ( -fTmp <= fA00 ) + { + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + else // region 8 (corner) + { + if ( -fB0 < fA00 ) + { + if(fB0>=0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + } + else + { + if ( fT >= 0.0f ) + { + if ( fT <= fDet ) // region 5 (side) + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + else // region 4 (corner) + { + fTmp = fA01+fB0; + if ( fTmp < 0.0f ) + { + if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + else // region 6 (corner) + { + if ( fB0 < 0.0f ) + { + if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + } + else + { + // line segments are parallel + if ( fA01 > 0.0f ) + { + // direction vectors form an obtuse angle + if ( fB0 >= 0.0f ) + { + fSqrDist = fC; + } + else if ( -fB0 <= fA00 ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + fTmp = fA00+fB0; + if ( -fTmp >= fA01 ) + { + fSqrDist = fA00+fA11+fC+2.0f*(fA01+fB0+fB1); + } + else + { + fT = -fTmp/fA01; + fSqrDist = fA00+2.0f*fB0+fC+fT*(fA11*fT+2.0f*(fA01+fB1)); + } + } + } + else + { + // direction vectors form an acute angle + if ( -fB0 >= fA00 ) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else if ( fB0 <= 0.0f ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + if ( fB0 >= -fA01 ) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = -fB0/fA01; + fSqrDist = fC+fT*(2.0f*fB1+fA11*fT); + } + } + } + } + return fabsf(fSqrDist); +} + +inline_ float OPC_SegmentRaySqrDist(const Segment& rkSeg0, const Ray& rkSeg1) +{ + return OPC_SegmentSegmentSqrDist(rkSeg0, Segment(rkSeg1.mOrig, rkSeg1.mOrig + rkSeg1.mDir)); +} + +static float OPC_SegmentTriangleSqrDist(const Segment& segment, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + const Point TriEdge0 = p1 - p0; + const Point TriEdge1 = p2 - p0; + + const Point& rkSegOrigin = segment.GetOrigin(); + Point rkSegDirection = segment.ComputeDirection(); + + Point kDiff = p0 - rkSegOrigin; + float fA00 = rkSegDirection.SquareMagnitude(); + float fA01 = -rkSegDirection.Dot(TriEdge0); + float fA02 = -rkSegDirection.Dot(TriEdge1); + float fA11 = TriEdge0.SquareMagnitude(); + float fA12 = TriEdge0.Dot(TriEdge1); + float fA22 = TriEdge1.Dot(TriEdge1); + float fB0 = -kDiff.Dot(rkSegDirection); + float fB1 = kDiff.Dot(TriEdge0); + float fB2 = kDiff.Dot(TriEdge1); + float fCof00 = fA11*fA22-fA12*fA12; + float fCof01 = fA02*fA12-fA01*fA22; + float fCof02 = fA01*fA12-fA02*fA11; + float fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + Ray kTriSeg; + Point kPt; + float fSqrDist, fSqrDist0; + + if(fabsf(fDet)>=gs_fTolerance) + { + float fCof11 = fA00*fA22-fA02*fA02; + float fCof12 = fA02*fA01-fA00*fA12; + float fCof22 = fA00*fA11-fA01*fA01; + float fInvDet = 1.0f/fDet; + float fRhs0 = -fB0*fInvDet; + float fRhs1 = -fB1*fInvDet; + float fRhs2 = -fB2*fInvDet; + + float fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + float fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + float fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if ( fR < 0.0f ) + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4m + { + // min on face s=0 or t=0 or r=0 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0 1 + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4p + { + // min on face s=0 or t=0 or r=1 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0GetTriangle(triangle_index); + * // Setup pointers to vertices for the collision system + * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]); + * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]); + * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]); + * } + * + * // Setup callbacks + * MeshInterface0->SetCallback(ColCallback, udword(Mesh0)); + * MeshInterface1->SetCallback(ColCallback, udword(Mesh1)); + * \endcode + * + * Of course, you should make this callback as fast as possible. And you're also not supposed + * to modify the geometry *after* the collision trees have been built. The alternative was to + * store the geometry & topology in the collision system as well (as in RAPID) but we have found + * this approach to waste a lot of ram in many cases. + * + * + * POINTERS: + * + * If you're internally using the following canonical structures: + * - a vertex made of three 32-bits floating point values + * - a triangle made of three 32-bits integer vertex references + * ...then you may want to use pointers instead of callbacks. This is the same, except OPCODE will directly + * use provided pointers to access the topology and geometry, without using a callback. It might be faster, + * but probably not as safe. Pointers have been introduced in OPCODE 1.2. + * + * Ex: + * + * \code + * // Setup pointers + * MeshInterface0->SetPointers(Mesh0->GetFaces(), Mesh0->GetVerts()); + * MeshInterface1->SetPointers(Mesh1->GetFaces(), Mesh1->GetVerts()); + * \endcode + * + * + * STRIDES: + * + * If your vertices are D3D-like entities interleaving a position, a normal and/or texture coordinates + * (i.e. if your vertices are FVFs), you might want to use a vertex stride to skip extra data OPCODE + * doesn't need. Using a stride shouldn't be notably slower than not using it, but it might increase + * cache misses. Please also note that you *shouldn't* read from AGP or video-memory buffers ! + * + * + * In any case, compilation flags are here to select callbacks/pointers/strides at compile time, so + * choose what's best for your application. All of this has been wrapped into this MeshInterface. + * + * \class MeshInterface + * \author Pierre Terdiman + * \version 1.3 + * \date November, 27, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::MeshInterface() : +#ifdef OPC_USE_CALLBACKS + mUserData (null), + mObjCallback (null), +#else + mTris (null), + mVerts (null), + #ifdef OPC_USE_STRIDE + mTriStride (sizeof(IndexedTriangle)), + mVertexStride (sizeof(Point)), + #endif +#endif + mNbTris (0), + mNbVerts (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::~MeshInterface() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool MeshInterface::IsValid() const +{ + if(!mNbTris || !mNbVerts) return false; +#ifdef OPC_USE_CALLBACKS + if(!mObjCallback) return false; +#else + if(!mTris || !mVerts) return false; +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword MeshInterface::CheckTopology() const +{ + // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases. + // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner + // you can try this: www.codercorner.com/Consolidation.zip + + udword NbDegenerate = 0; + + VertexPointers VP; + + // Using callbacks, we don't have access to vertex indices. Nevertheless we still can check for + // redundant vertex pointers, which cover all possibilities (callbacks/pointers/strides). + for(udword i=0;i= 0.0f; + } + }; + +#ifdef OPC_USE_CALLBACKS + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to request vertices from the app. + * \param triangle_index [in] face index for which the system is requesting the vertices + * \param triangle [out] triangle's vertices (must be provided by the user) + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*RequestCallback) (udword triangle_index, VertexPointers& triangle, void* user_data); +#endif + + class OPCODE_API MeshInterface + { + public: + // Constructor / Destructor + MeshInterface(); + ~MeshInterface(); + // Common settings + inline_ udword GetNbTriangles() const { return mNbTris; } + inline_ udword GetNbVertices() const { return mNbVerts; } + inline_ void SetNbTriangles(udword nb) { mNbTris = nb; } + inline_ void SetNbVertices(udword nb) { mNbVerts = nb; } + +#ifdef OPC_USE_CALLBACKS + // Callback settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \param user_data [in] user-defined data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetCallback(RequestCallback callback, void* user_data); + inline_ void* GetUserData() const { return mUserData; } + inline_ RequestCallback GetCallback() const { return mObjCallback; } +#else + // Pointers settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object. + * \param tris [in] pointer to triangles + * \param verts [in] pointer to vertices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetPointers(const IndexedTriangle* tris, const Point* verts); + inline_ const IndexedTriangle* GetTris() const { return mTris; } + inline_ const Point* GetVerts() const { return mVerts; } + + #ifdef OPC_USE_STRIDE + // Strides settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Strides control + * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices. + * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(Point) bytes are used to get vertex position. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(Point)); + inline_ udword GetTriStride() const { return mTriStride; } + inline_ udword GetVertexStride() const { return mVertexStride; } + #endif +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Fetches a triangle given a triangle index. + * \param vp [out] required triangle's vertex pointers + * \param index [in] triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void GetTriangle(VertexPointers& vp, udword index) const + { +#ifdef OPC_USE_CALLBACKS + (mObjCallback)(index, vp, mUserData); +#else + #ifdef OPC_USE_STRIDE + const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride); + vp.Vertex[0] = (const Point*)(((ubyte*)mVerts) + T->mVRef[0] * mVertexStride); + vp.Vertex[1] = (const Point*)(((ubyte*)mVerts) + T->mVRef[1] * mVertexStride); + vp.Vertex[2] = (const Point*)(((ubyte*)mVerts) + T->mVRef[2] * mVertexStride); + #else + const IndexedTriangle* T = &mTris[index]; + vp.Vertex[0] = &mVerts[T->mVRef[0]]; + vp.Vertex[1] = &mVerts[T->mVRef[1]]; + vp.Vertex[2] = &mVerts[T->mVRef[2]]; + #endif +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Remaps client's mesh according to a permutation. + * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles) + * \param permutation [in] list of triangle indices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool RemapClient(udword nb_indices, const udword* permutation) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool IsValid() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + udword CheckTopology() const; + private: + + udword mNbTris; //!< Number of triangles in the input model + udword mNbVerts; //!< Number of vertices in the input model +#ifdef OPC_USE_CALLBACKS + // User callback + void* mUserData; //!< User-defined data sent to callback + RequestCallback mObjCallback; //!< Object callback +#else + // User pointers + const IndexedTriangle* mTris; //!< Array of indexed triangles + const Point* mVerts; //!< Array of vertices + #ifdef OPC_USE_STRIDE + udword mTriStride; //!< Possible triangle stride in bytes [Opcode 1.3] + udword mVertexStride; //!< Possible vertex stride in bytes [Opcode 1.3] + #endif +#endif + }; + +#endif //__OPC_MESHINTERFACE_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_Model.cpp b/OPC_Model.cpp similarity index 59% rename from OpcodeDistrib/OPC_Model.cpp rename to OPC_Model.cpp index 0c1582a..0616c4d 100644 --- a/OpcodeDistrib/OPC_Model.cpp +++ b/OPC_Model.cpp @@ -25,16 +25,17 @@ * * Usage: * - * 1) Build an OPCODE_Model using a creation structure: + * 1) Create a static mesh interface using callbacks or pointers. (see OPC_MeshInterface.cpp). + * Keep it around in your app, since a pointer to this interface is saved internally and + * used until you release the collision structures. + * + * 2) Build a Model using a creation structure: * * \code - * OPCODE_Model Sample; + * Model Sample; * * OPCODECREATE OPCC; - * OPCC.NbTris = ...; - * OPCC.NbVerts = ...; - * OPCC.Tris = ...; - * OPCC.Verts = ...; + * OPCC.IMesh = ...; * OPCC.Rules = ...; * OPCC.NoLeaf = ...; * OPCC.Quantized = ...; @@ -42,7 +43,7 @@ * bool Status = Sample.Build(OPCC); * \endcode * - * 2) Create a tree collider and setup it: + * 3) Create a tree collider and set it up: * * \code * AABBTreeCollider TC; @@ -52,44 +53,6 @@ * TC.SetTemporalCoherence(...); * \endcode * - * 3) Setup object callbacks. Geometry & topology are NOT stored in the collision system, - * in order to save some ram. So, when the system needs them to perform accurate intersection - * tests, you're requested to provide the triangle-vertices corresponding to a given face index. - * - * Ex: - * - * \code - * static void ColCallback(udword triangleindex, VertexPointers& triangle, udword user_data) - * { - * // Get back Mesh0 or Mesh1 (you also can use 2 different callbacks) - * Mesh* MyMesh = (Mesh*)user_data; - * // Get correct triangle in the app-controlled database - * const Triangle* Tri = MyMesh->GetTriangle(triangleindex); - * // Setup pointers to vertices for the collision system - * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]); - * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]); - * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]); - * } - * - * // Setup callbacks - * TC.SetCallback0(ColCallback, udword(Mesh0)); - * TC.SetCallback1(ColCallback, udword(Mesh1)); - * \endcode - * - * Of course, you should make this callback as fast as possible. And you're also not supposed - * to modify the geometry *after* the collision trees have been built. The alternative was to - * store the geometry & topology in the collision system as well (as in RAPID) but we have found - * this approach to waste a lot of ram in many cases. - * - * Since version 1.2 you can also use plain pointers. It's a tiny bit faster, but not as safe. - * - * Ex: - * - * \code - * TC.SetPointers0(Mesh0->GetFaces(), Mesh0->GetVerts()); - * TC.SetPointers1(Mesh1->GetFaces(), Mesh1->GetVerts()); - * \endcode - * * 4) Perform a collision query * * \code @@ -118,9 +81,9 @@ * TC.GetNbBVPrimTests() = number of Triangle-BV overlap tests performed during last query * \endcode * - * \class OPCODE_Model + * \class Model * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -131,27 +94,12 @@ using namespace Opcode; -OPCODECREATE::OPCODECREATE() -{ - NbTris = 0; - NbVerts = 0; - Tris = null; - Verts = null; - Rules = SPLIT_COMPLETE | SPLIT_LARGESTAXIS; - NoLeaf = true; - Quantized = true; -#ifdef __MESHMERIZER_H__ - CollisionHull = false; -#endif // __MESHMERIZER_H__ - KeepOriginal = false; -} - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -OPCODE_Model::OPCODE_Model() : mSource(null), mTree(null), mNoLeaf(false), mQuantized(false) +Model::Model() { #ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! mHull = null; @@ -163,10 +111,19 @@ OPCODE_Model::OPCODE_Model() : mSource(null), mTree(null), mNoLeaf(false), mQuan * Destructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -OPCODE_Model::~OPCODE_Model() +Model::~Model() { - DELETESINGLE(mSource); - DELETESINGLE(mTree); + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the model. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Model::Release() +{ + ReleaseBase(); #ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! DELETESINGLE(mHull); #endif // __MESHMERIZER_H__ @@ -179,78 +136,87 @@ OPCODE_Model::~OPCODE_Model() * \return true if success */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OPCODE_Model::Build(const OPCODECREATE& create) +bool Model::Build(const OPCODECREATE& create) { // 1) Checkings - if(!create.NbTris || !create.Tris || !create.Verts) return false; + if(!create.mIMesh || !create.mIMesh->IsValid()) return false; - // In this lib, we only support complete trees - if(!(create.Rules&SPLIT_COMPLETE)) return SetIceError("OPCODE WARNING: supports complete trees only! Use SPLIT_COMPLETE.\n"); + // For this model, we only support complete trees + if(create.mSettings.mLimit!=1) return SetIceError("OPCODE WARNING: supports complete trees only! Use mLimit = 1.\n", null); - // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases. - // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner - // you can try this: www.codercorner.com/Consolidation.zip - const IndexedTriangle* Tris = (const IndexedTriangle*)create.Tris; - udword NbDegenerate = 0; - for(udword i=0;iCheckTopology(); if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); // We continue nonetheless.... + Release(); // Make sure previous tree has been discarded [Opcode 1.3, thanks Adam] + + // 1-1) Setup mesh interface automatically [Opcode 1.3] + SetMeshInterface(create.mIMesh); + + // Special case for 1-triangle meshes [Opcode 1.3] + udword NbTris = create.mIMesh->GetNbTriangles(); + if(NbTris==1) + { + // We don't need to actually create a tree here, since we'll only have a single triangle to deal with anyway. + // It's a waste to use a "model" for this but at least it will work. + mModelCode |= OPC_SINGLE_NODE; + return true; + } + // 2) Build a generic AABB Tree. mSource = new AABBTree; CHECKALLOC(mSource); // 2-1) Setup a builder. Our primitives here are triangles from input mesh, // so we use an AABBTreeOfTrianglesBuilder..... - AABBTreeOfTrianglesBuilder TB; - TB.mTriList = Tris; - TB.mVerts = create.Verts; - TB.mRules = create.Rules; - TB.mNbPrimitives = create.NbTris; - if(!mSource->Build(&TB)) return false; - - // 3) Create an optimized tree according to user-settings - // 3-1) Create the correct class - mNoLeaf = create.NoLeaf; - mQuantized = create.Quantized; - - if(mNoLeaf) { - if(mQuantized) mTree = new AABBQuantizedNoLeafTree; - else mTree = new AABBNoLeafTree; + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mSettings = create.mSettings; + TB.mNbPrimitives = NbTris; + if(!mSource->Build(&TB)) return false; } - else - { - if(mQuantized) mTree = new AABBQuantizedTree; - else mTree = new AABBCollisionTree; - } - CHECKALLOC(mTree); + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) return false; // 3-2) Create optimized tree if(!mTree->Build(mSource)) return false; // 3-3) Delete generic tree if needed - if(!create.KeepOriginal) DELETESINGLE(mSource); + if(!create.mKeepOriginal) DELETESINGLE(mSource); #ifdef __MESHMERIZER_H__ // 4) Convex hull - if(create.CollisionHull) + if(create.mCollisionHull) { // Create hull mHull = new CollisionHull; CHECKALLOC(mHull); CONVEXHULLCREATE CHC; - CHC.NbVerts = create.NbVerts; - CHC.Vertices = create.Verts; + // ### doesn't work with strides + CHC.NbVerts = create.mIMesh->GetNbVertices(); + CHC.Vertices = create.mIMesh->GetVerts(); CHC.UnifyNormals = true; CHC.ReduceVertices = true; CHC.WordFaces = false; mHull->Compute(CHC); } #endif // __MESHMERIZER_H__ + return true; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword Model::GetUsedBytes() const +{ + if(!mTree) return 0; + return mTree->GetUsedBytes(); +} diff --git a/OPC_Model.h b/OPC_Model.h new file mode 100644 index 0000000..1d7e1e4 --- /dev/null +++ b/OPC_Model.h @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for OPCODE models. + * \file OPC_Model.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_MODEL_H__ +#define __OPC_MODEL_H__ + + class OPCODE_API Model : public BaseModel + { + public: + // Constructor/Destructor + Model(); + virtual ~Model(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Build(const OPCODECREATE& create); + +#ifdef __MESHMERIZER_H__ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the collision hull. + * \return the collision hull if it exists + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const CollisionHull* GetHull() const { return mHull; } +#endif // __MESHMERIZER_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) udword GetUsedBytes() const; + + private: +#ifdef __MESHMERIZER_H__ + CollisionHull* mHull; //!< Possible convex hull +#endif // __MESHMERIZER_H__ + // Internal methods + void Release(); + }; + +#endif //__OPC_MODEL_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_OBBCollider.cpp b/OPC_OBBCollider.cpp similarity index 52% rename from OpcodeDistrib/OPC_OBBCollider.cpp rename to OPC_OBBCollider.cpp index ba1dde2..a784339 100644 --- a/OpcodeDistrib/OPC_OBBCollider.cpp +++ b/OPC_OBBCollider.cpp @@ -21,7 +21,7 @@ * * \class OBBCollider * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date January, 1st, 2002 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -35,38 +35,24 @@ using namespace Opcode; #include "OPC_BoxBoxOverlap.h" #include "OPC_TriBoxOverlap.h" +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + //! OBB-triangle test -#ifdef OPC_USE_CALLBACKS - #define OBB_PRIM(primindex) \ - /* Request vertices from the app */ \ - VertexPointers VP; (mObjCallback)(primindex, VP, mUserData); \ - /* Transform them in a common space */ \ - TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \ - TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \ - TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \ - /* Perform triangle-box overlap test */ \ - if(TriBoxOverlap()) \ - { \ - /* Set contact status */ \ - mFlags |= OPC_CONTACT; \ - mTouchedPrimitives->Add(primindex); \ - } -#else - #define OBB_PRIM(primindex) \ - /* Direct access to vertices */ \ - const IndexedTriangle* T = &mFaces[primindex]; \ - /* Transform them in a common space */ \ - TransformPoint(mLeafVerts[0], mVerts[T->mVRef[0]], mRModelToBox, mTModelToBox); \ - TransformPoint(mLeafVerts[1], mVerts[T->mVRef[1]], mRModelToBox, mTModelToBox); \ - TransformPoint(mLeafVerts[2], mVerts[T->mVRef[2]], mRModelToBox, mTModelToBox); \ - /* Perform triangle-box overlap test */ \ - if(TriBoxOverlap()) \ - { \ - /* Set contact status */ \ - mFlags |= OPC_CONTACT; \ - mTouchedPrimitives->Add(primindex); \ - } -#endif +#define OBB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -103,8 +89,8 @@ const char* OBBCollider::ValidateSettings() /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a box cache * \param box [in] collision OBB in local space @@ -115,22 +101,62 @@ const char* OBBCollider::ValidateSettings() * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBBCollider::Collide(OBBCache& cache, const OBB& box, OPCODE_Model* model, const Matrix4x4* worldb, const Matrix4x4* worldm) +bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm) { // Checkings - if(!model) return false; + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; - // Simple double-dispatch - if(!model->HasLeafNodes()) + if(!model.HasLeafNodes()) { - if(model->IsQuantized()) return Collide(cache, box, (const AABBQuantizedNoLeafTree*)model->GetTree(), worldb, worldm); - else return Collide(cache, box, (const AABBNoLeafTree*)model->GetTree(), worldb, worldm); + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } else { - if(model->IsQuantized()) return Collide(cache, box, (const AABBQuantizedTree*)model->GetTree(), worldb, worldm); - else return Collide(cache, box, (const AABBCollisionTree*)model->GetTree(), worldb, worldm); + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } + + return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -144,7 +170,7 @@ bool OBBCollider::Collide(OBBCache& cache, const OBB& box, OPCODE_Model* model, * \param box [in] obb in local space * \param worldb [in] obb's world matrix, or null * \param worldm [in] model's world matrix, or null - * \return contact status + * \return TRUE if we can return immediately * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -160,12 +186,12 @@ BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* wo if(worldb) { - WorldB = Matrix4x4( box.mRot2 * Matrix3x3(*worldb) ); + WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) ); WorldB.SetTrans(box.mCenter * *worldb); } else { - WorldB = box.mRot2; + WorldB = box.mRot; WorldB.SetTrans(box.mCenter); } @@ -190,40 +216,26 @@ BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* wo mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel); } - // Precompute absolute box-to-model rotation matrix - for(udword i=0;i<3;i++) + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) { - for(udword j=0;j<3;j++) + if(!SkipPrimitiveTests()) { - // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) - mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]); - } - } - - // Precompute bounds for box-in-box test - mB0 = mBoxExtents - mTModelToBox; - mB1 = - mBoxExtents - mTModelToBox; + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); - // Precompute box-box data - Courtesy of Erwin de Vries - Point Box = mBoxExtents; - mBBx1 = Box.x*mAR.m[0][0] + Box.y*mAR.m[1][0] + Box.z*mAR.m[2][0]; - mBBy1 = Box.x*mAR.m[0][1] + Box.y*mAR.m[1][1] + Box.z*mAR.m[2][1]; - mBBz1 = Box.x*mAR.m[0][2] + Box.y*mAR.m[1][2] + Box.z*mAR.m[2][2]; - - mBB_1 = Box.y*mAR.m[2][0] + Box.z*mAR.m[1][0]; - mBB_2 = Box.x*mAR.m[2][0] + Box.z*mAR.m[0][0]; - mBB_3 = Box.x*mAR.m[1][0] + Box.y*mAR.m[0][0]; - mBB_4 = Box.y*mAR.m[2][1] + Box.z*mAR.m[1][1]; - mBB_5 = Box.x*mAR.m[2][1] + Box.z*mAR.m[0][1]; - mBB_6 = Box.x*mAR.m[1][1] + Box.y*mAR.m[0][1]; - mBB_7 = Box.y*mAR.m[2][2] + Box.z*mAR.m[1][2]; - mBB_8 = Box.x*mAR.m[2][2] + Box.z*mAR.m[0][2]; - mBB_9 = Box.x*mAR.m[1][2] + Box.y*mAR.m[0][2]; + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + OBB_PRIM(udword(0), OPC_CONTACT) - // 3) Setup destination pointer - mTouchedPrimitives = &cache.TouchedPrimitives; + // Return immediately regardless of status + return TRUE; + } + } - // 4) Check temporal coherence: + // 5) Check temporal coherence: if(TemporalCoherenceEnabled()) { // Here we use temporal coherence @@ -242,152 +254,85 @@ BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* wo mTouchedPrimitives->Reset(); // Perform overlap test between the cached triangle and the box (and set contact status if needed) - OBB_PRIM(PreviouslyTouchedFace) + OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; } // else no face has been touched during previous query // => we'll have to perform a normal query } - else mTouchedPrimitives->Reset(); - } - else - { - // Here we don't use temporal coherence => do a normal query - mTouchedPrimitives->Reset(); - } - - return GetContactStatus(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for normal trees. - * \param cache [in/out] a box cache - * \param box [in] collision OBB in local space - * \param tree [in] model's AABB tree - * \param worldb [in] OBB's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const AABBCollisionTree* tree, const Matrix4x4* worldb, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box, worldb, worldm)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for no-leaf trees. - * \param cache [in/out] a box cache - * \param box [in] collision OBB in local space - * \param tree [in] model's AABB tree - * \param worldb [in] OBB's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const AABBNoLeafTree* tree, const Matrix4x4* worldb, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, box, worldb, worldm)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} + else + { + // ### rewrite this + OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel); -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized trees. - * \param cache [in/out] a box cache - * \param box [in] collision OBB in local space - * \param tree [in] model's AABB tree - * \param worldb [in] OBB's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const AABBQuantizedTree* tree, const Matrix4x4* worldb, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif + // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): + if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged - // Init collision query - if(InitQuery(cache, box, worldb, worldm)) return true; + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N - // Perform collision query - _Collide(tree->GetNodes()); + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); - return true; -} + // Make a fat box so that coherence will work for subsequent frames + TestBox.mExtents *= cache.FatCoeff; + mBoxExtents *= cache.FatCoeff; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized no-leaf trees. - * \param cache [in/out] a box cache - * \param box [in] collision OBB in local space - * \param tree [in] model's AABB tree - * \param worldb [in] OBB's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* worldb, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif + // Update cache with query data (signature for cached faces) + cache.FatBox = TestBox; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } - // Init collision query - if(InitQuery(cache, box, worldb, worldm)) return true; + // Now we can precompute box-box data - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; + // Precompute absolute box-to-model rotation matrix + for(udword i=0;i<3;i++) + { + for(udword j=0;j<3;j++) + { + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]); + } + } - // Perform collision query - _Collide(tree->GetNodes()); + // Precompute bounds for box-in-box test + mB0 = mBoxExtents - mTModelToBox; + mB1 = - mBoxExtents - mTModelToBox; - return true; + // Precompute box-box data - Courtesy of Erwin de Vries + mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0]; + mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1]; + mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2]; + + mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0]; + mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0]; + mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0]; + mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1]; + mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1]; + mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1]; + mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2]; + mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2]; + mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2]; + + return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -448,7 +393,7 @@ inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be) return TRUE; } -#define TEST_OBB_IN_BOX(center, extents) \ +#define TEST_BOX_IN_OBB(center, extents) \ if(OBBContainsBox(center, extents)) \ { \ /* Set contact status */ \ @@ -468,11 +413,11 @@ void OBBCollider::_Collide(const AABBCollisionNode* node) // Perform OBB-AABB overlap test if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; - TEST_OBB_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) if(node->IsLeaf()) { - OBB_PRIM(node->GetPrimitive()) + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { @@ -484,6 +429,33 @@ void OBBCollider::_Collide(const AABBCollisionNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized AABB trees. @@ -493,18 +465,18 @@ void OBBCollider::_Collide(const AABBCollisionNode* node) void OBBCollider::_Collide(const AABBQuantizedNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform OBB-AABB overlap test if(!BoxBoxOverlap(Extents, Center)) return; - TEST_OBB_IN_BOX(Center, Extents) + TEST_BOX_IN_OBB(Center, Extents) if(node->IsLeaf()) { - OBB_PRIM(node->GetPrimitive()) + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { @@ -516,6 +488,38 @@ void OBBCollider::_Collide(const AABBQuantizedNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for no-leaf AABB trees. @@ -527,17 +531,39 @@ void OBBCollider::_Collide(const AABBNoLeafNode* node) // Perform OBB-AABB overlap test if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; - TEST_OBB_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) - if(node->HasLeaf()) { OBB_PRIM(node->GetPrimitive()) } + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { OBB_PRIM(node->GetPrimitive2()) } + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized no-leaf AABB trees. @@ -547,20 +573,195 @@ void OBBCollider::_Collide(const AABBNoLeafNode* node) void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform OBB-AABB overlap test if(!BoxBoxOverlap(Extents, Center)) return; - TEST_OBB_IN_BOX(Center, Extents) + TEST_BOX_IN_OBB(Center, Extents) - if(node->HasLeaf()) { OBB_PRIM(node->GetPrimitive()) } + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { OBB_PRIM(node->GetPrimitive2()) } + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::HybridOBBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::~HybridOBBCollider() +{ +} + +bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/OpcodeDistrib/OPC_OBBCollider.h b/OPC_OBBCollider.h similarity index 80% rename from OpcodeDistrib/OPC_OBBCollider.h rename to OPC_OBBCollider.h index 1da4157..b5c1de2 100644 --- a/OpcodeDistrib/OPC_OBBCollider.h +++ b/OPC_OBBCollider.h @@ -22,9 +22,17 @@ struct OPCODE_API OBBCache : VolumeCache { - OBBCache() + OBBCache() : FatCoeff(1.1f) { + FatBox.mCenter.Zero(); + FatBox.mExtents.Zero(); + FatBox.mRot.Identity(); } + + // Cached faces signature + OBB FatBox; //!< Box used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< extents multiplier used to create a fat box }; class OPCODE_API OBBCollider : public VolumeCollider @@ -33,14 +41,13 @@ // Constructor / Destructor OBBCollider(); virtual ~OBBCollider(); - // Generic collision query /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a box cache * \param box [in] collision OBB in local space @@ -51,13 +58,8 @@ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Collide(OBBCache& cache, const OBB& box, OPCODE_Model* model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + bool Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); - // Collision queries - bool Collide(OBBCache& cache, const OBB& box, const AABBCollisionTree* tree, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); - bool Collide(OBBCache& cache, const OBB& box, const AABBNoLeafTree* tree, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); - bool Collide(OBBCache& cache, const OBB& box, const AABBQuantizedTree* tree, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); - bool Collide(OBBCache& cache, const OBB& box, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); // Settings /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -113,6 +115,10 @@ void _Collide(const AABBNoLeafNode* node); void _Collide(const AABBQuantizedNode* node); void _Collide(const AABBQuantizedNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); // Overlap tests inline_ BOOL OBBContainsBox(const Point& bc, const Point& be); inline_ BOOL BoxBoxOverlap(const Point& extents, const Point& center); @@ -121,4 +127,16 @@ BOOL InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); }; + class OPCODE_API HybridOBBCollider : public OBBCollider + { + public: + // Constructor / Destructor + HybridOBBCollider(); + virtual ~HybridOBBCollider(); + + bool Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + #endif // __OPC_OBBCOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_OptimizedTree.cpp b/OPC_OptimizedTree.cpp similarity index 56% rename from OpcodeDistrib/OPC_OptimizedTree.cpp rename to OPC_OptimizedTree.cpp index 99cceba..aed4f64 100644 --- a/OpcodeDistrib/OPC_OptimizedTree.cpp +++ b/OPC_OptimizedTree.cpp @@ -8,7 +8,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains code for optimized trees. + * Contains code for optimized trees. Implements 4 trees: + * - normal + * - no leaf + * - quantized + * - no leaf / quantized + * * \file OPC_OptimizedTree.cpp * \author Pierre Terdiman * \date March, 20, 2001 @@ -21,7 +26,7 @@ * * \class AABBCollisionTree * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,7 +37,7 @@ * * \class AABBNoLeafTree * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -43,7 +48,7 @@ * * \class AABBQuantizedTree * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -54,7 +59,7 @@ * * \class AABBQuantizedNoLeafTree * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -84,42 +89,42 @@ static bool gFixQuantized = true; * else remaining bits are a P-node pointer, and N = P + 1 * * \relates AABBCollisionNode - * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword boxid, udword& curid, const AABBTreeNode* curnode) - * \param linear [in] base address of destination nodes - * \param boxid [in] index of destination node - * \param curid [in] current running index - * \param curnode [in] current node from input tree + * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void _BuildCollisionTree(AABBCollisionNode* linear, const udword boxid, udword& curid, const AABBTreeNode* curnode) +static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) { - // Current node from input tree is "curnode". Must be flattened into "linear[boxid]". + // Current node from input tree is "current_node". Must be flattened into "linear[boxid]". // Store the AABB - curnode->GetAABB()->GetCenter(linear[boxid].mAABB.mCenter); - curnode->GetAABB()->GetExtents(linear[boxid].mAABB.mExtents); + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); // Store remaining info - if(curnode->IsLeaf()) + if(current_node->IsLeaf()) { // The input tree must be complete => i.e. one primitive/leaf - ASSERT(curnode->GetNbPrimitives()==1); + ASSERT(current_node->GetNbPrimitives()==1); // Get the primitive index from the input tree - udword PrimitiveIndex = curnode->GetPrimitives()[0]; + udword PrimitiveIndex = current_node->GetPrimitives()[0]; // Setup box data as the primitive index, marked as leaf - linear[boxid].mData = (PrimitiveIndex<<1)|1; + linear[box_id].mData = (PrimitiveIndex<<1)|1; } else { // To make the negative one implicit, we must store P and N in successive order - udword PosID = curid++; // Get a new id for positive child - udword NegID = curid++; // Get a new id for negative child + udword PosID = current_id++; // Get a new id for positive child + udword NegID = current_id++; // Get a new id for negative child // Setup box data as the forthcoming new P pointer - linear[boxid].mData = (udword)&linear[PosID]; + linear[box_id].mData = (udword)&linear[PosID]; // Make sure it's not marked as leaf - ASSERT(!(linear[boxid].mData&1)); + ASSERT(!(linear[box_id].mData&1)); // Recurse with new IDs - _BuildCollisionTree(linear, PosID, curid, curnode->GetPos()); - _BuildCollisionTree(linear, NegID, curid, curnode->GetNeg()); + _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos()); + _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg()); } } @@ -135,23 +140,23 @@ static void _BuildCollisionTree(AABBCollisionNode* linear, const udword boxid, u * - N pointer => a node (LSB=0) or a primitive (LSB=1) * * \relates AABBNoLeafNode - * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword boxid, udword& curid, const AABBTreeNode* curnode) - * \param linear [in] base address of destination nodes - * \param boxid [in] index of destination node - * \param curid [in] current running index - * \param curnode [in] current node from input tree + * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword boxid, udword& curid, const AABBTreeNode* curnode) +static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) { - const AABBTreeNode* P = curnode->GetPos(); - const AABBTreeNode* N = curnode->GetNeg(); + const AABBTreeNode* P = current_node->GetPos(); + const AABBTreeNode* N = current_node->GetNeg(); // Leaf nodes here?! ASSERT(P); ASSERT(N); // Internal node => keep the box - curnode->GetAABB()->GetCenter(linear[boxid].mAABB.mCenter); - curnode->GetAABB()->GetExtents(linear[boxid].mAABB.mExtents); + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); if(P->IsLeaf()) { @@ -160,18 +165,18 @@ static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword boxid, udword& // Get the primitive index from the input tree udword PrimitiveIndex = P->GetPrimitives()[0]; // Setup prev box data as the primitive index, marked as leaf - linear[boxid].mData = (PrimitiveIndex<<1)|1; + linear[box_id].mPosData = (PrimitiveIndex<<1)|1; } else { // Get a new id for positive child - udword PosID = curid++; + udword PosID = current_id++; // Setup box data - linear[boxid].mData = (udword)&linear[PosID]; + linear[box_id].mPosData = (udword)&linear[PosID]; // Make sure it's not marked as leaf - ASSERT(!(linear[boxid].mData&1)); + ASSERT(!(linear[box_id].mPosData&1)); // Recurse - _BuildNoLeafTree(linear, PosID, curid, P); + _BuildNoLeafTree(linear, PosID, current_id, P); } if(N->IsLeaf()) @@ -181,18 +186,18 @@ static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword boxid, udword& // Get the primitive index from the input tree udword PrimitiveIndex = N->GetPrimitives()[0]; // Setup prev box data as the primitive index, marked as leaf - linear[boxid].mData2 = (PrimitiveIndex<<1)|1; + linear[box_id].mNegData = (PrimitiveIndex<<1)|1; } else { - // Get a new id for positive child - udword NegID = curid++; + // Get a new id for negative child + udword NegID = current_id++; // Setup box data - linear[boxid].mData2 = (udword)&linear[NegID]; + linear[box_id].mNegData = (udword)&linear[NegID]; // Make sure it's not marked as leaf - ASSERT(!(linear[boxid].mData2&1)); + ASSERT(!(linear[box_id].mNegData&1)); // Recurse - _BuildNoLeafTree(linear, NegID, curid, N); + _BuildNoLeafTree(linear, NegID, current_id, N); } } @@ -232,23 +237,65 @@ bool AABBCollisionTree::Build(AABBTree* tree) if(NbNodes!=NbTriangles*2-1) return false; // Get nodes - mNbNodes = NbNodes; - mNodes = new AABBCollisionNode[mNbNodes]; - CHECKALLOC(mNodes); + if(mNbNodes!=NbNodes) // Same number of nodes => keep moving + { + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + mNodes = new AABBCollisionNode[mNbNodes]; + CHECKALLOC(mNodes); + } // Build the tree udword CurID = 1; _BuildCollisionTree(mNodes, 0, CurID, tree); ASSERT(CurID==mNbNodes); -#ifdef __ICECORE_H__ - Log("Original tree: %d nodes, depth %d\n", NbNodes, tree->ComputeDepth()); - Log("AABB Collision tree: %d nodes, %d bytes - Alignment: %d\n", mNbNodes, GetUsedBytes(), Alignment(udword(mNodes))); -#endif + return true; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Refit(const MeshInterface* mesh_interface) +{ + ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!"); + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->IsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); return true; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. @@ -285,20 +332,127 @@ bool AABBNoLeafTree::Build(AABBTree* tree) if(NbNodes!=NbTriangles*2-1) return false; // Get nodes - mNbNodes = NbTriangles-1; - mNodes = new AABBNoLeafNode[mNbNodes]; - CHECKALLOC(mNodes); + if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving + { + mNbNodes = NbTriangles-1; + DELETEARRAY(mNodes); + mNodes = new AABBNoLeafNode[mNbNodes]; + CHECKALLOC(mNodes); + } // Build the tree udword CurID = 1; _BuildNoLeafTree(mNodes, 0, CurID, tree); ASSERT(CurID==mNbNodes); -#ifdef __ICECORE_H__ - Log("Original tree: %d nodes, depth %d\n", NbNodes, tree->ComputeDepth()); - Log("AABB quantized tree: %d nodes, %d bytes - Alignment: %d\n", mNbNodes, GetUsedBytes(), Alignment(udword(mNodes))); + return true; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); #endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface) +{ + // Checkings + if(!mesh_interface) return false; + + // Bottom-up update + VertexPointers VP; + Point Min,Max; + Point Min_,Max_; + udword Index = mNbNodes; + while(Index--) + { + AABBNoLeafNode& Current = mNodes[Index]; + + if(Current.HasPosLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetPosPrimitive()); + ComputeMinMax(Min, Max, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + if(Current.HasNegLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetNegPrimitive()); + ComputeMinMax(Min_, Max_, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); return true; } @@ -315,7 +469,11 @@ bool AABBNoLeafTree::Build(AABBTree* tree) // - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed // number of quantization bits. Even better, could probably be best delta-encoded. -// Find max values (could use the first node only with min/max boxes) + +// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't. +// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal +// centers/extents in order to quantize them. The first node would only give a single center and +// a single extents. While extents would be the biggest, the center wouldn't. #define FIND_MAX_VALUES \ /* Get max values */ \ Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ @@ -330,26 +488,26 @@ bool AABBNoLeafTree::Build(AABBTree* tree) if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \ } -#define INIT_QUANTIZATION \ - udword nbc=15; /* Keep one bit for sign */ \ - udword nbe=15; /* Keep one bit for fix */ \ - if(!gFixQuantized) nbe++; \ - \ - /* Compute quantization coeffs */ \ - Point CQuantCoeff, EQuantCoeff; \ - CQuantCoeff.x = float((1<ComputeDepth()); - Log("AABB quantized tree: %d nodes, %d bytes - Alignment: %d\n", mNbNodes, GetUsedBytes(), Alignment(udword(mNodes))); -#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedTree::Refit(const MeshInterface* mesh_interface) +{ + ASSERT(!"Not implemented since requantizing is painful !"); + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBQuantizedNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->IsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); return true; } @@ -510,6 +707,7 @@ bool AABBQuantizedNoLeafTree::Build(AABBTree* tree) // Get nodes mNbNodes = NbTriangles-1; + DELETEARRAY(mNodes); AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes]; CHECKALLOC(Nodes); @@ -531,20 +729,54 @@ bool AABBQuantizedNoLeafTree::Build(AABBTree* tree) // Quantize udword Data; - for(i=0;iComputeDepth()); - Log("AABB quantized no-leaf tree: %d nodes, %d bytes - Alignment: %d\n", mNbNodes, GetUsedBytes(), Alignment(udword(mNodes))); -#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedNoLeafTree::Refit(const MeshInterface* mesh_interface) +{ + ASSERT(!"Not implemented since requantizing is painful !"); + return false; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBQuantizedNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); return true; } diff --git a/OPC_OptimizedTree.h b/OPC_OptimizedTree.h new file mode 100644 index 0000000..7bfe06f --- /dev/null +++ b/OPC_OptimizedTree.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for optimized trees. + * \file OPC_OptimizedTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_OPTIMIZEDTREE_H__ +#define __OPC_OPTIMIZEDTREE_H__ + + //! Common interface for a node of an implicit tree + #define IMPLEMENT_IMPLICIT_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf test */ \ + inline_ BOOL IsLeaf() const { return mData&1; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mData; } \ + inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \ + inline_ udword GetPrimitive() const { return (mData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + udword mData; + + //! Common interface for a node of a no-leaf tree + #define IMPLEMENT_NOLEAF_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mPosData(0), mNegData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf tests */ \ + inline_ BOOL HasPosLeaf() const { return mPosData&1; } \ + inline_ BOOL HasNegLeaf() const { return mNegData&1; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \ + inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \ + inline_ udword GetPosPrimitive() const { return (mPosData>>1); } \ + inline_ udword GetNegPrimitive() const { return (mNegData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + udword mPosData; \ + udword mNegData; + + class OPCODE_API AABBCollisionNode + { + IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB) + + inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; } + inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); } + inline_ udword GetRadius() const + { + udword* Bits = (udword*)&mAABB.mExtents.x; + udword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + + // NB: using the square-magnitude or the true volume of the box, seems to yield better results + // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size" + // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's + // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is + // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices + // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very + // good strategy. + }; + + class OPCODE_API AABBQuantizedNode + { + IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB) + + inline_ uword GetSize() const + { + const uword* Bits = mAABB.mExtents; + uword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all + // over the place.......! + }; + + class OPCODE_API AABBNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB) + }; + + class OPCODE_API AABBQuantizedNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB) + }; + + //! Common interface for a collision tree + #define IMPLEMENT_COLLISION_TREE(base_class, node) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + virtual ~base_class(); \ + /* Builds from a standard tree */ \ + override(AABBOptimizedTree) bool Build(AABBTree* tree); \ + /* Refits the tree */ \ + override(AABBOptimizedTree) bool Refit(const MeshInterface* mesh_interface); \ + /* Walks the tree */ \ + override(AABBOptimizedTree) bool Walk(GenericWalkingCallback callback, void* user_data) const; \ + /* Data access */ \ + inline_ const node* GetNodes() const { return mNodes; } \ + /* Stats */ \ + override(AABBOptimizedTree) udword GetUsedBytes() const { return mNbNodes*sizeof(node); } \ + private: \ + node* mNodes; + + typedef bool (*GenericWalkingCallback) (const void* current, void* user_data); + + class OPCODE_API AABBOptimizedTree + { + public: + // Constructor / Destructor + AABBOptimizedTree() : + mNbNodes (0) + {} + virtual ~AABBOptimizedTree() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(AABBTree* tree) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(const MeshInterface* mesh_interface) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Walk(GenericWalkingCallback callback, void* user_data) const = 0; + + // Data access + virtual udword GetUsedBytes() const = 0; + inline_ udword GetNbNodes() const { return mNbNodes; } + + protected: + udword mNbNodes; + }; + + class OPCODE_API AABBCollisionTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode) + }; + + class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode) + }; + + class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + + class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + +#endif // __OPC_OPTIMIZEDTREE_H__ diff --git a/OPC_Picking.cpp b/OPC_Picking.cpp new file mode 100644 index 0000000..1d6319f --- /dev/null +++ b/OPC_Picking.cpp @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#ifdef OPC_RAYHIT_CALLBACK + +/* + Possible RayCollider usages: + - boolean query (shadow feeler) + - closest hit + - all hits + - number of intersection (boolean) + +*/ + +bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts) +{ + struct Local + { + static void AllContacts(const CollisionFace& hit, void* user_data) + { + CollisionFaces* CF = (CollisionFaces*)user_data; + CF->AddFace(hit); + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::AllContacts); + collider.SetUserData(&contacts); + return true; +} + +bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact) +{ + struct Local + { + static void ClosestContact(const CollisionFace& hit, void* user_data) + { + CollisionFace* CF = (CollisionFace*)user_data; + if(hit.mDistancemDistance) *CF = hit; + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::ClosestContact); + collider.SetUserData(&closest_contact); + closest_contact.mDistance = MAX_FLOAT; + return true; +} + +bool Opcode::SetupShadowFeeler(RayCollider& collider) +{ + collider.SetFirstContact(true); + collider.SetHitCallback(null); + return true; +} + +bool Opcode::SetupInOutTest(RayCollider& collider) +{ + collider.SetFirstContact(false); + collider.SetHitCallback(null); + // Results with collider.GetNbIntersections() + return true; +} + +bool Opcode::Picking( +CollisionFace& picked_face, +const Ray& world_ray, const Model& model, const Matrix4x4* world, +float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data) +{ + struct Local + { + struct CullData + { + CollisionFace* Closest; + float MinLimit; + CullModeCallback Callback; + void* UserData; + Point ViewPoint; + const MeshInterface* IMesh; + }; + + // Called for each stabbed face + static void RenderCullingCallback(const CollisionFace& hit, void* user_data) + { + CullData* Data = (CullData*)user_data; + + // Discard face if we already have a closer hit + if(hit.mDistance>=Data->Closest->mDistance) return; + + // Discard face if hit point is smaller than min limit. This mainly happens when the face is in front + // of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an + // object that he may not even be able to see, which is very annoying. + if(hit.mDistance<=Data->MinLimit) return; + + // This is the index of currently stabbed triangle. + udword StabbedFaceIndex = hit.mFaceID; + + // We may keep it or not, depending on backface culling + bool KeepIt = true; + + // Catch *render* cull mode for this face + CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData); + + if(CM!=CULLMODE_NONE) // Don't even compute culling for double-sided triangles + { + // Compute backface culling for current face + + VertexPointers VP; + Data->IMesh->GetTriangle(VP, StabbedFaceIndex); + if(VP.BackfaceCulling(Data->ViewPoint)) + { + if(CM==CULLMODE_CW) KeepIt = false; + } + else + { + if(CM==CULLMODE_CCW) KeepIt = false; + } + } + + if(KeepIt) *Data->Closest = hit; + } + }; + + RayCollider RC; + RC.SetMaxDist(max_dist); + RC.SetTemporalCoherence(false); + RC.SetCulling(false); // We need all faces since some of them can be double-sided + RC.SetFirstContact(false); + RC.SetHitCallback(Local::RenderCullingCallback); + + picked_face.mFaceID = INVALID_ID; + picked_face.mDistance = MAX_FLOAT; + picked_face.mU = 0.0f; + picked_face.mV = 0.0f; + + Local::CullData Data; + Data.Closest = &picked_face; + Data.MinLimit = min_dist; + Data.Callback = callback; + Data.UserData = user_data; + Data.ViewPoint = view_point; + Data.IMesh = model.GetMeshInterface(); + + if(world) + { + // Get matrices + Matrix4x4 InvWorld; + InvertPRMatrix(InvWorld, *world); + + // Compute camera position in mesh space + Data.ViewPoint *= InvWorld; + } + + RC.SetUserData(&Data); + if(RC.Collide(world_ray, model, world)) + { + return picked_face.mFaceID!=INVALID_ID; + } + return false; +} + +#endif diff --git a/OPC_Picking.h b/OPC_Picking.h new file mode 100644 index 0000000..74a87b2 --- /dev/null +++ b/OPC_Picking.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_PICKING_H__ +#define __OPC_PICKING_H__ + +#ifdef OPC_RAYHIT_CALLBACK + + enum CullMode + { + CULLMODE_NONE = 0, + CULLMODE_CW = 1, + CULLMODE_CCW = 2 + }; + + typedef CullMode (*CullModeCallback)(udword triangle_index, void* user_data); + + OPCODE_API bool SetupAllHits (RayCollider& collider, CollisionFaces& contacts); + OPCODE_API bool SetupClosestHit (RayCollider& collider, CollisionFace& closest_contact); + OPCODE_API bool SetupShadowFeeler (RayCollider& collider); + OPCODE_API bool SetupInOutTest (RayCollider& collider); + + OPCODE_API bool Picking( + CollisionFace& picked_face, + const Ray& world_ray, const Model& model, const Matrix4x4* world, + float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data); +#endif + +#endif //__OPC_PICKING_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_PlanesAABBOverlap.h b/OPC_PlanesAABBOverlap.h similarity index 100% rename from OpcodeDistrib/OPC_PlanesAABBOverlap.h rename to OPC_PlanesAABBOverlap.h diff --git a/OPC_PlanesCollider.cpp b/OPC_PlanesCollider.cpp new file mode 100644 index 0000000..89b507c --- /dev/null +++ b/OPC_PlanesCollider.cpp @@ -0,0 +1,653 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a planes collider. + * \file OPC_PlanesCollider.cpp + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a Planes-vs-tree collider. + * + * \class PlanesCollider + * \author Pierre Terdiman + * \version 1.3 + * \date January, 1st, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_PlanesAABBOverlap.h" +#include "OPC_PlanesTriOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + +//! Planes-triangle test +#define PLANES_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + mIMesh->GetTriangle(mVP, prim_index); \ + /* Perform triangle-box overlap test */ \ + if(PlanesTriOverlap(clip_mask)) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::PlanesCollider() : + mPlanes (null), + mNbPlanes (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::~PlanesCollider() +{ + DELETEARRAY(mPlanes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* PlanesCollider::ValidateSettings() +{ + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; + + return VolumeCollider::ValidateSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes in world space + * \param nb_planes [in] number of planes + * \param model [in] Opcode model to collide with + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const Model& model, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + udword PlaneMask = (1<mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - compute planes in model space + * - check temporal coherence + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes + * \param nb_planes [in] number of planes + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL PlanesCollider::InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute planes in model space + if(nb_planes>mNbPlanes) + { + DELETEARRAY(mPlanes); + mPlanes = new Plane[nb_planes]; + } + mNbPlanes = nb_planes; + + if(worldm) + { + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + +// for(udword i=0;iHasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the planes (and set contact status if needed) + udword clip_mask = (1< check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the planes (and set contact status if needed) + udword clip_mask = (1< we'll have to perform a normal query + } + else mTouchedPrimitives->Reset(); + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +#define TEST_CLIP_MASK \ + /* If the box is completely included, so are its children. We don't need to do extra tests, we */ \ + /* can immediately output a list of visible children. Those ones won't need to be clipped. */ \ + if(!OutClipMask) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::HybridPlanesCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::~HybridPlanesCollider() +{ +} + +bool HybridPlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const HybridModel& model, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + udword clip_mask = (1<mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + udword clip_mask = (1<GetNbFaces()) \ - { \ - mStabbedFace.mFaceID = prim; \ - mStabbedFaces->AddFace(mStabbedFace); \ - } \ - else \ - { \ - CollisionFace* Current = const_cast(mStabbedFaces->GetFaces()); \ - if(Current && mStabbedFace.mDistancemDistance) \ - { \ - mStabbedFace.mFaceID = prim; \ - *Current = mStabbedFace; \ - } \ - } \ - } +#define SET_CONTACT(prim_index, flag) \ + mNbIntersections++; \ + /* Set contact status */ \ + mFlags |= flag; \ + /* In any case the contact has been found and recorded in mStabbedFace */ \ + mStabbedFace.mFaceID = prim_index; + +#ifdef OPC_RAYHIT_CALLBACK -#ifdef OPC_USE_CALLBACKS - #define STAB_PRIM(prim) \ - /* Request vertices from the app */ \ - VertexPointers VP; (mObjCallback)(prim, VP, mUserData); \ + #define HANDLE_CONTACT(prim_index, flag) \ + SET_CONTACT(prim_index, flag) \ \ - /* Perform ray-tri overlap test and return */ \ - if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ - { \ - /* Intersection point is valid if: */ \ - /* - distance is positive (else it can just be a face behind the orig point) */ \ - /* - distance is smaller than a given max distance (useful for shadow feelers) */ \ - if(!IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) \ - { \ - mNbIntersections++; \ - if(IR(mStabbedFace.mDistance)mVRef[0]], mVerts[Tri->mVRef[1]], mVerts[Tri->mVRef[2]])) \ + /* Now we can also record it in mStabbedFaces if available */ \ + if(mStabbedFaces) \ { \ - /* Intersection point is valid if: */ \ - /* - distance is positive (else it can just be a face behind the orig point) */ \ - /* - distance is smaller than a given max distance (useful for shadow feelers) */ \ - if(!IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) \ + /* If we want all faces or if that's the first one we hit */ \ + if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \ + { \ + mStabbedFaces->AddFace(mStabbedFace); \ + } \ + else \ { \ - mNbIntersections++; \ - if(IR(mStabbedFace.mDistance)(mStabbedFaces->GetFaces()); \ + if(Current && mStabbedFace.mDistancemDistance) \ { \ - HANDLE_CONTACT(prim) \ + *Current = mStabbedFace; \ } \ } \ } -#endif -#ifdef OPC_USE_CALLBACKS - #define UNBOUNDED_STAB_PRIM(prim) \ - /* Request vertices from the app */ \ - VertexPointers VP; (mObjCallback)(prim, VP, mUserData); \ - \ - /* Perform ray-tri overlap test and return */ \ - if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ - { \ - /* Intersection point is valid if: */ \ - /* - distance is positive (else it can just be a face behind the orig point) */ \ - if(!IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) \ - { \ - mNbIntersections++; \ - HANDLE_CONTACT(prim) \ - } \ - } -#else - #define UNBOUNDED_STAB_PRIM(prim) \ - const IndexedTriangle* Tri = &mFaces[prim]; \ - \ - /* Perform ray-tri overlap test and return */ \ - if(RayTriOverlap(mVerts[Tri->mVRef[0]], mVerts[Tri->mVRef[1]], mVerts[Tri->mVRef[2]])) \ - { \ - /* Intersection point is valid if: */ \ - /* - distance is positive (else it can just be a face behind the orig point) */ \ - if(!IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) \ - { \ - mNbIntersections++; \ - HANDLE_CONTACT(prim) \ - } \ + #define UPDATE_CACHE \ + if(cache && GetContactStatus() && mStabbedFaces) \ + { \ + const CollisionFace* Current = mStabbedFaces->GetFaces(); \ + if(Current) *cache = Current->mFaceID; \ + else *cache = INVALID_ID; \ } #endif +#define SEGMENT_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + /* Intersection point is valid if dist < segment's length */ \ + /* We know dist>0 so we can use integers */ \ + if(IR(mStabbedFace.mDistance)GetTriangle(VP, prim_index); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + HANDLE_CONTACT(prim_index, flag) \ + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -RayCollider::RayCollider() - : mNbRayBVTests (0), +RayCollider::RayCollider() : + mNbRayBVTests (0), mNbRayPrimTests (0), mNbIntersections (0), - mClosestHit (false), mCulling (true), -#ifdef OPC_USE_CALLBACKS +#ifdef OPC_RAYHIT_CALLBACK + mHitCallback (null), mUserData (0), - mObjCallback (null), #else - mFaces (null), - mVerts (null), -#endif + mClosestHit (false), mStabbedFaces (null), +#endif mMaxDist (MAX_FLOAT) { } @@ -232,14 +235,13 @@ RayCollider::~RayCollider() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char* RayCollider::ValidateSettings() { -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return "Object callback must be defined! Call: SetCallback()."; -#else - if(!mFaces || !mVerts) return "Object pointers must be defined! Call: SetPointers()."; -#endif if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; +#ifndef OPC_RAYHIT_CALLBACK if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!"; + if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!"; +#endif + if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)"; return null; } @@ -257,24 +259,67 @@ const char* RayCollider::ValidateSettings() * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool RayCollider::Collide(const Ray& world_ray, OPCODE_Model* model, const Matrix4x4* world, udword* cache) +bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache) { // Checkings - if(!model) return false; + if(!Setup(&model)) return false; - // Simple double-dispatch - if(!model->HasLeafNodes()) + // Init collision query + if(InitQuery(world_ray, world, cache)) return true; + + if(!model.HasLeafNodes()) { - if(model->IsQuantized()) return Collide(world_ray, (const AABBQuantizedNoLeafTree*)model->GetTree(), world, cache); - else return Collide(world_ray, (const AABBNoLeafTree*)model->GetTree(), world, cache); + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } } else { - if(model->IsQuantized()) return Collide(world_ray, (const AABBQuantizedTree*)model->GetTree(), world, cache); - else return Collide(world_ray, (const AABBCollisionTree*)model->GetTree(), world, cache); + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } } + + // Update cache if needed + UPDATE_CACHE + return true; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Initializes a stabbing query : @@ -282,24 +327,26 @@ bool RayCollider::Collide(const Ray& world_ray, OPCODE_Model* model, const Matri * - compute ray in local space * - check temporal coherence * - * \param world_ray [in] stabbing ray in world space - * \param world [in] object's world matrix, or null - * \param faceid [in] index of previously stabbed triangle - * \return contact status + * \param world_ray [in] stabbing ray in world space + * \param world [in] object's world matrix, or null + * \param face_id [in] index of previously stabbed triangle + * \return TRUE if we can return immediately * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* faceid) +BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id) { // Reset stats & contact status Collider::InitQuery(); mNbRayBVTests = 0; mNbRayPrimTests = 0; mNbIntersections = 0; +#ifndef OPC_RAYHIT_CALLBACK if(mStabbedFaces) mStabbedFaces->Reset(); +#endif // Compute ray in local space - // The (Origin/Dir) form is needed for the ray-triangle test anyway + // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests) if(world) { Matrix3x3 InvWorld = *world; @@ -315,7 +362,65 @@ BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword mOrigin = world_ray.mOrig; } - // Precompute data + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + if(!SkipPrimitiveTests()) + { + // Perform overlap test between the unique triangle and the ray (and set contact status if needed) + SEGMENT_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // Check temporal coherence : + + // Test previously colliding primitives first + if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID) + { +#ifdef OLD_CODE +#ifndef OPC_RAYHIT_CALLBACK + if(!mClosestHit) +#endif + { + // Request vertices from the app + VertexPointers VP; + mIMesh->GetTriangle(VP, *face_id); + // Perform ray-cached tri overlap test + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Intersection point is valid if: + // - distance is positive (else it can just be a face behind the orig point) + // - distance is smaller than a given max distance (useful for shadow feelers) +// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistanceAddFace(mStabbedFace); +#endif + return TRUE; + } + } + } +#else + // New code + // We handle both Segment/ray queries with the same segment code, and a possible infinite limit + SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; +#endif + } + + // Precompute data (moved after temporal coherence since only needed for ray-AABB) if(IR(mMaxDist)!=IEEE_MAX_FLOAT) { // For Segment-AABB overlap @@ -343,188 +448,7 @@ BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword mFDir.z = fabsf(mDir.z); } - // Check temporal coherence : - -//## voir mClosest - - // Test previously colliding primitives first - if(TemporalCoherenceEnabled() && FirstContactEnabled() && faceid && *faceid!=INVALID_ID) - { - // Request vertices from the app - VertexPointers VP; -#ifdef OPC_USE_CALLBACKS - (mObjCallback)(*faceid, VP, mUserData); -#else - const IndexedTriangle* Tri = &mFaces[*faceid]; - VP.Vertex[0] = &mVerts[Tri->mVRef[0]]; - VP.Vertex[1] = &mVerts[Tri->mVRef[1]]; - VP.Vertex[2] = &mVerts[Tri->mVRef[2]]; -#endif - // Perform ray-cached tri overlap - if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) - { - // Intersection point is valid if: - // - distance is positive (else it can just be a face behind the orig point) - // - distance is smaller than a given max distance (useful for shadow feelers) - if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistanceAddFace(mStabbedFace); - } - } - } - return GetContactStatus(); -} - -#define UPDATE_CACHE \ - if(cache && GetContactStatus() && mStabbedFaces) \ - { \ - const CollisionFace* Current = mStabbedFaces->GetFaces(); \ - if(Current) *cache = Current->mFaceID; \ - else *cache = INVALID_ID; \ - } - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Stabbing query for normal trees. - * \param world_ray [in] stabbing ray in world space - * \param tree [in] object's AABB tree - * \param world [in] object's world matrix, or null - * \param cache [in] a possibly cached face index, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool RayCollider::Collide(const Ray& world_ray, const AABBCollisionTree* tree, const Matrix4x4* world, udword* cache) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - // Init collision query - if(InitQuery(world_ray, world, cache)) return true; - - // Perform stabbing query - if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _Stab(tree->GetNodes()); - else _UnboundedStab(tree->GetNodes()); - - // Update cache if needed - UPDATE_CACHE - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Stabbing query for no-leaf trees. - * \param world_ray [in] stabbing ray in world space - * \param tree [in] object's AABB tree - * \param world [in] object's world matrix, or null - * \param cache [in] a possibly cached face index, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool RayCollider::Collide(const Ray& world_ray, const AABBNoLeafTree* tree, const Matrix4x4* world, udword* cache) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(world_ray, world, cache)) return true; - - // Perform stabbing query - if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _Stab(tree->GetNodes()); - else _UnboundedStab(tree->GetNodes()); - - // Update cache if needed - UPDATE_CACHE - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Stabbing query for quantized trees. - * \param world_ray [in] stabbing ray in world space - * \param tree [in] object's AABB tree - * \param world [in] object's world matrix, or null - * \param cache [in] a possibly cached face index, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool RayCollider::Collide(const Ray& world_ray, const AABBQuantizedTree* tree, const Matrix4x4* world, udword* cache) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(world_ray, world, cache)) return true; - - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform stabbing query - if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _Stab(tree->GetNodes()); - else _UnboundedStab(tree->GetNodes()); - - // Update cache if needed - UPDATE_CACHE - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Stabbing query for quantized no-leaf trees. - * \param world_ray [in] stabbing ray in world space - * \param tree [in] object's AABB tree - * \param world [in] object's world matrix, or null - * \param cache [in] a possibly cached face index, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool RayCollider::Collide(const Ray& world_ray, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* world, udword* cache) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(world_ray, world, cache)) return true; - - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform stabbing query - if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _Stab(tree->GetNodes()); - else _UnboundedStab(tree->GetNodes()); - - // Update cache if needed - UPDATE_CACHE - return true; + return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -553,8 +477,8 @@ bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& if(InitQuery(world_ray)) return true; // Perform stabbing query - if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _Stab(tree, box_indices); - else _UnboundedStab(tree, box_indices); + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices); + else _RayStab(tree, box_indices); return true; } @@ -566,22 +490,22 @@ bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_Stab(const AABBCollisionNode* node) +void RayCollider::_SegmentStab(const AABBCollisionNode* node) { // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->IsLeaf()) { - STAB_PRIM(node->GetPrimitive()) + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { - _Stab(node->GetPos()); + _SegmentStab(node->GetPos()); if(ContactFound()) return; - _Stab(node->GetNeg()); + _SegmentStab(node->GetNeg()); } } @@ -591,27 +515,27 @@ void RayCollider::_Stab(const AABBCollisionNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_Stab(const AABBQuantizedNode* node) +void RayCollider::_SegmentStab(const AABBQuantizedNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { - STAB_PRIM(node->GetPrimitive()) + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { - _Stab(node->GetPos()); + _SegmentStab(node->GetPos()); if(ContactFound()) return; - _Stab(node->GetNeg()); + _SegmentStab(node->GetNeg()); } } @@ -621,24 +545,24 @@ void RayCollider::_Stab(const AABBQuantizedNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_Stab(const AABBNoLeafNode* node) +void RayCollider::_SegmentStab(const AABBNoLeafNode* node) { // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; - if(node->HasLeaf()) + if(node->HasPosLeaf()) { - STAB_PRIM(node->GetPrimitive()) + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } - else _Stab(node->GetPos()); + else _SegmentStab(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) + if(node->HasNegLeaf()) { - STAB_PRIM(node->GetPrimitive2()) + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } - else _Stab(node->GetNeg()); + else _SegmentStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -647,29 +571,29 @@ void RayCollider::_Stab(const AABBNoLeafNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_Stab(const AABBQuantizedNoLeafNode* node) +void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(Center, Extents)) return; - if(node->HasLeaf()) + if(node->HasPosLeaf()) { - STAB_PRIM(node->GetPrimitive()) + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } - else _Stab(node->GetPos()); + else _SegmentStab(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) + if(node->HasNegLeaf()) { - STAB_PRIM(node->GetPrimitive2()) + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } - else _Stab(node->GetNeg()); + else _SegmentStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -679,7 +603,7 @@ void RayCollider::_Stab(const AABBQuantizedNoLeafNode* node) * \param box_indices [out] indices of stabbed boxes */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_Stab(const AABBTreeNode* node, Container& box_indices) +void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices) { // Test the box against the segment Point Center, Extents; @@ -693,8 +617,8 @@ void RayCollider::_Stab(const AABBTreeNode* node, Container& box_indices) } else { - _Stab(node->GetPos(), box_indices); - _Stab(node->GetNeg(), box_indices); + _SegmentStab(node->GetPos(), box_indices); + _SegmentStab(node->GetNeg(), box_indices); } } @@ -704,22 +628,22 @@ void RayCollider::_Stab(const AABBTreeNode* node, Container& box_indices) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_UnboundedStab(const AABBCollisionNode* node) +void RayCollider::_RayStab(const AABBCollisionNode* node) { // Perform Ray-AABB overlap test if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->IsLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive()) + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { - _UnboundedStab(node->GetPos()); + _RayStab(node->GetPos()); if(ContactFound()) return; - _UnboundedStab(node->GetNeg()); + _RayStab(node->GetNeg()); } } @@ -729,27 +653,27 @@ void RayCollider::_UnboundedStab(const AABBCollisionNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_UnboundedStab(const AABBQuantizedNode* node) +void RayCollider::_RayStab(const AABBQuantizedNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Ray-AABB overlap test if(!RayAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive()) + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { - _UnboundedStab(node->GetPos()); + _RayStab(node->GetPos()); if(ContactFound()) return; - _UnboundedStab(node->GetNeg()); + _RayStab(node->GetNeg()); } } @@ -759,24 +683,24 @@ void RayCollider::_UnboundedStab(const AABBQuantizedNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_UnboundedStab(const AABBNoLeafNode* node) +void RayCollider::_RayStab(const AABBNoLeafNode* node) { // Perform Ray-AABB overlap test if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; - if(node->HasLeaf()) + if(node->HasPosLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive()) + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } - else _UnboundedStab(node->GetPos()); + else _RayStab(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) + if(node->HasNegLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive2()) + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } - else _UnboundedStab(node->GetNeg()); + else _RayStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -785,29 +709,29 @@ void RayCollider::_UnboundedStab(const AABBNoLeafNode* node) * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_UnboundedStab(const AABBQuantizedNoLeafNode* node) +void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Ray-AABB overlap test if(!RayAABBOverlap(Center, Extents)) return; - if(node->HasLeaf()) + if(node->HasPosLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive()) + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } - else _UnboundedStab(node->GetPos()); + else _RayStab(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) + if(node->HasNegLeaf()) { - UNBOUNDED_STAB_PRIM(node->GetPrimitive2()) + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } - else _UnboundedStab(node->GetNeg()); + else _RayStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -817,7 +741,7 @@ void RayCollider::_UnboundedStab(const AABBQuantizedNoLeafNode* node) * \param box_indices [out] indices of stabbed boxes */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void RayCollider::_UnboundedStab(const AABBTreeNode* node, Container& box_indices) +void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices) { // Test the box against the ray Point Center, Extents; @@ -827,11 +751,12 @@ void RayCollider::_UnboundedStab(const AABBTreeNode* node, Container& box_indice if(node->IsLeaf()) { + mFlags |= OPC_CONTACT; box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); } else { - _UnboundedStab(node->GetPos(), box_indices); - _UnboundedStab(node->GetNeg(), box_indices); + _RayStab(node->GetPos(), box_indices); + _RayStab(node->GetNeg(), box_indices); } } diff --git a/OpcodeDistrib/OPC_RayCollider.h b/OPC_RayCollider.h similarity index 74% rename from OpcodeDistrib/OPC_RayCollider.h rename to OPC_RayCollider.h index 40af393..f3b0065 100644 --- a/OpcodeDistrib/OPC_RayCollider.h +++ b/OPC_RayCollider.h @@ -20,13 +20,52 @@ #ifndef __OPC_RAYCOLLIDER_H__ #define __OPC_RAYCOLLIDER_H__ + class OPCODE_API CollisionFace + { + public: + //! Constructor + inline_ CollisionFace() {} + //! Destructor + inline_ ~CollisionFace() {} + + udword mFaceID; //!< Index of touched face + float mDistance; //!< Distance from collider to hitpoint + float mU, mV; //!< Impact barycentric coordinates + }; + + class OPCODE_API CollisionFaces : private Container + { + public: + //! Constructor + CollisionFaces() {} + //! Destructor + ~CollisionFaces() {} + + inline_ udword GetNbFaces() const { return GetNbEntries()>>2; } + inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); } + + inline_ void Reset() { Container::Reset(); } + + inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); } + }; + +#ifdef OPC_RAYHIT_CALLBACK + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to record a hit. + * \param hit [in] current hit + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*HitCallback) (const CollisionFace& hit, void* user_data); +#endif + class OPCODE_API RayCollider : public Collider { public: // Constructor / Destructor RayCollider(); virtual ~RayCollider(); - // Generic collision query /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -42,33 +81,29 @@ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Collide(const Ray& world_ray, OPCODE_Model* model, const Matrix4x4* world=null, udword* cache=null); - - // Collision queries - bool Collide(const Ray& world_ray, const AABBCollisionTree* tree, const Matrix4x4* world=null, udword* cache=null); - bool Collide(const Ray& world_ray, const AABBNoLeafTree* tree, const Matrix4x4* world=null, udword* cache=null); - bool Collide(const Ray& world_ray, const AABBQuantizedTree* tree, const Matrix4x4* world=null, udword* cache=null); - bool Collide(const Ray& world_ray, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* world=null, udword* cache=null); + bool Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world=null, udword* cache=null); + // bool Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices); // Settings +#ifndef OPC_RAYHIT_CALLBACK /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Settings: enable or disable "closest hit" mode. * \param flag [in] true to report closest hit only * \see SetCulling(bool flag) - * \see SetMaxDist(float maxdist) + * \see SetMaxDist(float max_dist) * \see SetDestination(StabbedFaces* sf) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void SetClosestHit(bool flag) { mClosestHit = flag; } - +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Settings: enable or disable backface culling. * \param flag [in] true to enable backface culling * \see SetClosestHit(bool flag) - * \see SetMaxDist(float maxdist) + * \see SetMaxDist(float max_dist) * \see SetDestination(StabbedFaces* sf) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,25 +112,29 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Settings: sets the higher distance bound. - * \param maxdist [in] higher distance bound. Default = maximal value, for ray queries (else segment) + * \param max_dist [in] higher distance bound. Default = maximal value, for ray queries (else segment) * \see SetClosestHit(bool flag) * \see SetCulling(bool flag) * \see SetDestination(StabbedFaces* sf) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetMaxDist(float maxdist=MAX_FLOAT) { mMaxDist = maxdist; } + inline_ void SetMaxDist(float max_dist=MAX_FLOAT) { mMaxDist = max_dist; } +#ifdef OPC_RAYHIT_CALLBACK + inline_ void SetHitCallback(HitCallback cb) { mHitCallback = cb; } + inline_ void SetUserData(void* user_data) { mUserData = user_data; } +#else /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Settings: sets the destination array for stabbed faces. * \param cf [in] destination array, filled during queries * \see SetClosestHit(bool flag) * \see SetCulling(bool flag) - * \see SetMaxDist(float maxdist) + * \see SetMaxDist(float max_dist) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; } - +#endif // Stats /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -128,25 +167,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ udword GetNbIntersections() const { return mNbIntersections; } -#ifdef OPC_USE_CALLBACKS - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Callback control: a method to setup object callback. Must provide triangle-vertices for a given triangle index. - * \param callback [in] user-defined callback - * \param data [in] user-defined data - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetCallback(OPC_CALLBACK callback, udword data) { mObjCallback = callback; mUserData = data; } -#else - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Pointers control: a method to setup object pointers. Must provide access to faces and vertices for a given object. - * \param faces [in] pointer to faces - * \param verts [in] pointer to vertices - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetPointers(const IndexedTriangle* faces, const Point* verts) { mFaces = faces; mVerts = verts; } -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. @@ -163,15 +183,11 @@ Point mData, mData2; // Stabbed faces CollisionFace mStabbedFace; //!< Current stabbed face - CollisionFaces* mStabbedFaces; //!< List of stabbed faces -#ifdef OPC_USE_CALLBACKS - // User callback - udword mUserData; //!< User-defined data sent to callback - OPC_CALLBACK mObjCallback; //!< Object callback +#ifdef OPC_RAYHIT_CALLBACK + HitCallback mHitCallback; //!< Callback used to record a hit + void* mUserData; //!< User-defined data #else - // User pointers - const IndexedTriangle* mFaces; //!< User-defined faces - const Point* mVerts; //!< User-defined vertices + CollisionFaces* mStabbedFaces; //!< List of stabbed faces #endif // Stats udword mNbRayBVTests; //!< Number of Ray-BV tests @@ -183,25 +199,27 @@ Point mExtentsCoeff; // Settings float mMaxDist; //!< Valid segment on the ray +#ifndef OPC_RAYHIT_CALLBACK bool mClosestHit; //!< Report closest hit only +#endif bool mCulling; //!< Stab culled faces or not // Internal methods - void _Stab(const AABBCollisionNode* node); - void _Stab(const AABBNoLeafNode* node); - void _Stab(const AABBQuantizedNode* node); - void _Stab(const AABBQuantizedNoLeafNode* node); - void _Stab(const AABBTreeNode* node, Container& box_indices); - void _UnboundedStab(const AABBCollisionNode* node); - void _UnboundedStab(const AABBNoLeafNode* node); - void _UnboundedStab(const AABBQuantizedNode* node); - void _UnboundedStab(const AABBQuantizedNoLeafNode* node); - void _UnboundedStab(const AABBTreeNode* node, Container& box_indices); + void _SegmentStab(const AABBCollisionNode* node); + void _SegmentStab(const AABBNoLeafNode* node); + void _SegmentStab(const AABBQuantizedNode* node); + void _SegmentStab(const AABBQuantizedNoLeafNode* node); + void _SegmentStab(const AABBTreeNode* node, Container& box_indices); + void _RayStab(const AABBCollisionNode* node); + void _RayStab(const AABBNoLeafNode* node); + void _RayStab(const AABBQuantizedNode* node); + void _RayStab(const AABBQuantizedNoLeafNode* node); + void _RayStab(const AABBTreeNode* node, Container& box_indices); // Overlap tests inline_ BOOL RayAABBOverlap(const Point& center, const Point& extents); inline_ BOOL SegmentAABBOverlap(const Point& center, const Point& extents); inline_ BOOL RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); // Init methods - BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* faceid=null); + BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* face_id=null); }; #endif // __OPC_RAYCOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_RayTriOverlap.h b/OPC_RayTriOverlap.h similarity index 58% rename from OpcodeDistrib/OPC_RayTriOverlap.h rename to OPC_RayTriOverlap.h index 957c0a3..550effc 100644 --- a/OpcodeDistrib/OPC_RayTriOverlap.h +++ b/OPC_RayTriOverlap.h @@ -3,15 +3,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Computes a ray-triangle intersection test. - * From Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection" + * Original code from Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection". + * It's been optimized a bit with integer code, and modified to return a non-intersection if distance from + * ray origin to triangle is negative. * * \param vert0 [in] triangle vertex * \param vert1 [in] triangle vertex * \param vert2 [in] triangle vertex - * \param t [out] distance - * \param u [out] impact barycentric coordinate - * \param v [out] impact barycentric coordinate - * \return true on overlap + * \return true on overlap. mStabbedFace is filled with relevant info. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ BOOL RayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) @@ -40,45 +39,51 @@ inline_ BOOL RayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, // Calculate U parameter and test bounds mStabbedFace.mU = tvec|pvec; // if(IR(u)&0x80000000 || u>det) return FALSE; - if(IR(mStabbedFace.mU)&0x80000000 || IR(mStabbedFace.mU)>IR(det)) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IR(det)) return FALSE; // Prepare to test V parameter Point qvec = tvec^edge1; // Calculate V parameter and test bounds mStabbedFace.mV = mDir|qvec; - if(IR(mStabbedFace.mV)&0x80000000 || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE; // Calculate t, scale parameters, ray intersects triangle mStabbedFace.mDistance = edge2|qvec; - float inv_det = 1.0f / det; - mStabbedFace.mDistance *= inv_det; - mStabbedFace.mU *= inv_det; - mStabbedFace.mV *= inv_det; + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; + // Else go on + float OneOverDet = 1.0f / det; + mStabbedFace.mDistance *= OneOverDet; + mStabbedFace.mU *= OneOverDet; + mStabbedFace.mV *= OneOverDet; } else { // the non-culling branch if(det>-LOCAL_EPSILON && det1.0f) return FALSE; - if(IR(mStabbedFace.mU)&0x80000000 || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE; // prepare to test V parameter Point qvec = tvec^edge1; // Calculate V parameter and test bounds - mStabbedFace.mV = (mDir|qvec) * inv_det; - if(IR(mStabbedFace.mV)&0x80000000 || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE; + mStabbedFace.mV = (mDir|qvec) * OneOverDet; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE; // Calculate t, ray intersects triangle - mStabbedFace.mDistance = (edge2|qvec) * inv_det; + mStabbedFace.mDistance = (edge2|qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; } return TRUE; } diff --git a/OpcodeDistrib/OPC_Settings.h b/OPC_Settings.h similarity index 81% rename from OpcodeDistrib/OPC_Settings.h rename to OPC_Settings.h index 57fc5dd..8232d9b 100644 --- a/OpcodeDistrib/OPC_Settings.h +++ b/OPC_Settings.h @@ -32,9 +32,18 @@ //! Use tree-coherence or not [not implemented yet] // #define OPC_USE_TREE_COHERENCE - //! Use callbacks or direct pointers + //! Use callbacks or direct pointers. Using callbacks might be a bit slower (but probably not much) // #define OPC_USE_CALLBACKS + //! Support triangle and vertex strides or not. Using strides might be a bit slower (but probably not much) +// #define OPC_USE_STRIDE + + //! Discard negative pointer in vanilla trees + #define OPC_NO_NEG_VANILLA_TREE + + //! Use a callback in the ray collider + #define OPC_RAYHIT_CALLBACK + // NB: no compilation flag to enable/disable stats since they're actually needed in the box/box overlap test #endif //__OPC_SETTINGS_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_SphereAABBOverlap.h b/OPC_SphereAABBOverlap.h similarity index 100% rename from OpcodeDistrib/OPC_SphereAABBOverlap.h rename to OPC_SphereAABBOverlap.h diff --git a/OpcodeDistrib/OPC_SphereCollider.cpp b/OPC_SphereCollider.cpp similarity index 59% rename from OpcodeDistrib/OPC_SphereCollider.cpp rename to OPC_SphereCollider.cpp index cb30b71..4f2de83 100644 --- a/OpcodeDistrib/OPC_SphereCollider.cpp +++ b/OPC_SphereCollider.cpp @@ -25,7 +25,7 @@ * * \class SphereCollider * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date June, 2, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,32 +39,21 @@ using namespace Opcode; #include "OPC_SphereAABBOverlap.h" #include "OPC_SphereTriOverlap.h" +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + //! Sphere-triangle overlap test -#ifdef OPC_USE_CALLBACKS - #define SPHERE_PRIM(prim, flag) \ - /* Request vertices from the app */ \ - VertexPointers VP; (mObjCallback)(prim, VP, mUserData); \ - \ - /* Perform sphere-tri overlap test */ \ - if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ - { \ - /* Set contact status */ \ - mFlags |= flag; \ - mTouchedPrimitives->Add(prim); \ - } -#else - #define SPHERE_PRIM(prim, flag) \ - /* Direct access to vertices */ \ - const IndexedTriangle* Tri = &mFaces[prim]; \ - \ - /* Perform sphere-tri overlap test */ \ - if(SphereTriOverlap(mVerts[Tri->mVRef[0]], mVerts[Tri->mVRef[1]], mVerts[Tri->mVRef[2]])) \ - { \ - /* Set contact status */ \ - mFlags |= flag; \ - mTouchedPrimitives->Add(prim); \ - } -#endif +#define SPHERE_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform sphere-tri overlap test */ \ + if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -86,23 +75,12 @@ SphereCollider::~SphereCollider() { } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Validates current settings. You should call this method after all the settings and callbacks have been defined. - * \return null if everything is ok, else a string describing the problem - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const char* SphereCollider::ValidateSettings() -{ - return VolumeCollider::ValidateSettings(); -} - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a sphere cache * \param sphere [in] collision sphere in local space @@ -113,22 +91,61 @@ const char* SphereCollider::ValidateSettings() * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, OPCODE_Model* model, const Matrix4x4* worlds, const Matrix4x4* worldm) +bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm) { // Checkings - if(!model) return false; + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; - // Simple double-dispatch - if(!model->HasLeafNodes()) + if(!model.HasLeafNodes()) { - if(model->IsQuantized()) return Collide(cache, sphere, (const AABBQuantizedNoLeafTree*)model->GetTree(), worlds, worldm); - else return Collide(cache, sphere, (const AABBNoLeafTree*)model->GetTree(), worlds, worldm); + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } else { - if(model->IsQuantized()) return Collide(cache, sphere, (const AABBQuantizedTree*)model->GetTree(), worlds, worldm); - else return Collide(cache, sphere, (const AABBCollisionTree*)model->GetTree(), worlds, worldm); + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } } + return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -142,7 +159,7 @@ bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, OPCODE_Mo * \param sphere [in] sphere in local space * \param worlds [in] sphere's world matrix, or null * \param worldm [in] model's world matrix, or null - * \return contact status + * \return TRUE if we can return immediately * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -171,7 +188,23 @@ BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const M // 3) Setup destination pointer mTouchedPrimitives = &cache.TouchedPrimitives; - // 4) Check temporal coherence : + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the sphere (and set contact status if needed) + SPHERE_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : if(TemporalCoherenceEnabled()) { // Here we use temporal coherence @@ -191,6 +224,9 @@ BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const M // Perform overlap test between the cached triangle and the sphere (and set contact status if needed) SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; } // else no face has been touched during previous query // => we'll have to perform a normal query @@ -199,13 +235,16 @@ BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const M { // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious): float r = sqrtf(cache.FatRadius2) - sphere.mRadius; - if(cache.Center.SquareDistance(mCenter) < r*r) + if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r) { // - if N is included in P, return previous list // => we simply leave the list (mTouchedFaces) unchanged // Set contact status if needed if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; } else { @@ -216,6 +255,7 @@ BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const M // Make a fat sphere so that coherence will work for subsequent frames mRadius2 *= cache.FatCoeff; +// mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff); // Update cache with query data (signature for cached faces) cache.Center = mCenter; @@ -229,139 +269,7 @@ BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const M mTouchedPrimitives->Reset(); } - return GetContactStatus(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for normal trees. - * \param cache [in/out] a sphere cache - * \param sphere [in] collision sphere in local space - * \param tree [in] model's AABB tree - * \param worlds [in] sphere's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBCollisionTree* tree, const Matrix4x4* worlds, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, sphere, worlds, worldm)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for no-leaf trees. - * \param cache [in/out] a sphere cache - * \param sphere [in] collision sphere in local space - * \param tree [in] model's AABB tree - * \param worlds [in] sphere's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBNoLeafTree* tree, const Matrix4x4* worlds, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, sphere, worlds, worldm)) return true; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized trees. - * \param cache [in/out] a sphere cache - * \param sphere [in] collision sphere in local space - * \param tree [in] model's AABB tree - * \param worlds [in] sphere's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBQuantizedTree* tree, const Matrix4x4* worlds, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, sphere, worlds, worldm)) return true; - - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for quantized no-leaf trees. - * \param cache [in/out] a sphere cache - * \param sphere [in] collision sphere in local space - * \param tree [in] model's AABB tree - * \param worlds [in] sphere's world matrix, or null - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* worlds, const Matrix4x4* worldm) -{ - // Checkings - if(!tree) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, sphere, worlds, worldm)) return true; - - // Setup dequantization coeffs - mCenterCoeff = tree->mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes()); - - return true; + return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -417,7 +325,7 @@ inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be) return TRUE; } -#define TEST_SPHERE_IN_BOX(center, extents) \ +#define TEST_BOX_IN_SPHERE(center, extents) \ if(SphereContainsBox(center, extents)) \ { \ /* Set contact status */ \ @@ -437,7 +345,7 @@ void SphereCollider::_Collide(const AABBCollisionNode* node) // Perform Sphere-AABB overlap test if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; - TEST_SPHERE_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) if(node->IsLeaf()) { @@ -453,6 +361,33 @@ void SphereCollider::_Collide(const AABBCollisionNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized AABB trees. @@ -462,14 +397,14 @@ void SphereCollider::_Collide(const AABBCollisionNode* node) void SphereCollider::_Collide(const AABBQuantizedNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Sphere-AABB overlap test if(!SphereAABBOverlap(Center, Extents)) return; - TEST_SPHERE_IN_BOX(Center, Extents) + TEST_BOX_IN_SPHERE(Center, Extents) if(node->IsLeaf()) { @@ -485,6 +420,38 @@ void SphereCollider::_Collide(const AABBQuantizedNode* node) } } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for no-leaf AABB trees. @@ -496,17 +463,39 @@ void SphereCollider::_Collide(const AABBNoLeafNode* node) // Perform Sphere-AABB overlap test if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; - TEST_SPHERE_IN_BOX(node->mAABB.mCenter, node->mAABB.mExtents) + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) - if(node->HasLeaf()) { SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) } + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { SPHERE_PRIM(node->GetPrimitive2(), OPC_CONTACT) } + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for quantized no-leaf AABB trees. @@ -516,24 +505,51 @@ void SphereCollider::_Collide(const AABBNoLeafNode* node) void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node) { // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Sphere-AABB overlap test if(!SphereAABBOverlap(Center, Extents)) return; - TEST_SPHERE_IN_BOX(Center, Extents) + TEST_BOX_IN_SPHERE(Center, Extents) - if(node->HasLeaf()) { SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) } + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _Collide(node->GetPos()); if(ContactFound()) return; - if(node->HasLeaf2()) { SPHERE_PRIM(node->GetPrimitive2(), OPC_CONTACT) } + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _Collide(node->GetNeg()); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive collision query for vanilla AABB trees. @@ -548,19 +564,163 @@ void SphereCollider::_Collide(const AABBTreeNode* node) node->GetAABB()->GetExtents(Extents); if(!SphereAABBOverlap(Center, Extents)) return; - if(node->IsLeaf()) + if(node->IsLeaf() || SphereContainsBox(Center, Extents)) { + mFlags |= OPC_CONTACT; mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); } else { - if(SphereContainsBox(Center, Extents)) + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::HybridSphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::~HybridSphereCollider() +{ +} + +bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) { - mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); - return; + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); - _Collide(node->GetPos()); - _Collide(node->GetNeg()); + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; } diff --git a/OpcodeDistrib/OPC_SphereCollider.h b/OPC_SphereCollider.h similarity index 71% rename from OpcodeDistrib/OPC_SphereCollider.h rename to OPC_SphereCollider.h index ed275a5..aa53fdf 100644 --- a/OpcodeDistrib/OPC_SphereCollider.h +++ b/OPC_SphereCollider.h @@ -22,9 +22,8 @@ struct OPCODE_API SphereCache : VolumeCache { - SphereCache() : Center(0.0f,0.0f,0.0f), FatRadius2(0.0f), FatCoeff(1.1f) - { - } + SphereCache() : Center(0.0f,0.0f,0.0f), FatRadius2(0.0f), FatCoeff(1.1f) {} + ~SphereCache() {} // Cached faces signature Point Center; //!< Sphere used when performing the query resulting in cached faces @@ -39,13 +38,13 @@ // Constructor / Destructor SphereCollider(); virtual ~SphereCollider(); - // Generic collision query /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic collision query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() - * - in the user-provided destination array + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() * * \param cache [in/out] a sphere cache * \param sphere [in] collision sphere in local space @@ -56,24 +55,10 @@ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Collide(SphereCache& cache, const Sphere& sphere, OPCODE_Model* model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + bool Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); - // Collision queries - bool Collide(SphereCache& cache, const Sphere& sphere, const AABBCollisionTree* tree, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); - bool Collide(SphereCache& cache, const Sphere& sphere, const AABBNoLeafTree* tree, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); - bool Collide(SphereCache& cache, const Sphere& sphere, const AABBQuantizedTree* tree, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); - bool Collide(SphereCache& cache, const Sphere& sphere, const AABBQuantizedNoLeafTree* tree, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + // bool Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree); - // Settings - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. - * \return null if everything is ok, else a string describing the problem - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - override(Collider) const char* ValidateSettings(); - protected: // Sphere in model space Point mCenter; //!< Sphere center @@ -84,6 +69,10 @@ void _Collide(const AABBQuantizedNode* node); void _Collide(const AABBQuantizedNoLeafNode* node); void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); // Overlap tests inline_ BOOL SphereContainsBox(const Point& bc, const Point& be); inline_ BOOL SphereAABBOverlap(const Point& center, const Point& extents); @@ -92,4 +81,16 @@ BOOL InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); }; + class OPCODE_API HybridSphereCollider : public SphereCollider + { + public: + // Constructor / Destructor + HybridSphereCollider(); + virtual ~HybridSphereCollider(); + + bool Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + #endif // __OPC_SPHERECOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_SphereTriOverlap.h b/OPC_SphereTriOverlap.h similarity index 57% rename from OpcodeDistrib/OPC_SphereTriOverlap.h rename to OPC_SphereTriOverlap.h index 6838c2b..f1452ec 100644 --- a/OpcodeDistrib/OPC_SphereTriOverlap.h +++ b/OPC_SphereTriOverlap.h @@ -1,20 +1,40 @@ +// This is collision detection. If you do another distance test for collision *response*, +// if might be useful to simply *skip* the test below completely, and report a collision. +// - if sphere-triangle overlap, result is ok +// - if they don't, we'll discard them during collision response with a similar test anyway +// Overall this approach should run faster. + // Original code by David Eberly in Magic. BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) { // Stats mNbVolumePrimTests++; + // Early exit if one of the vertices is inside the sphere + Point kDiff = vert2 - mCenter; + float fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert1 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert0 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + // Else do the full distance test Point TriEdge0 = vert1 - vert0; Point TriEdge1 = vert2 - vert0; - Point kDiff = vert0 - mCenter; +//Point kDiff = vert0 - mCenter; float fA00 = TriEdge0.SquareMagnitude(); float fA01 = TriEdge0 | TriEdge1; float fA11 = TriEdge1.SquareMagnitude(); float fB0 = kDiff | TriEdge0; float fB1 = kDiff | TriEdge1; - float fC = kDiff.SquareMagnitude(); +//float fC = kDiff.SquareMagnitude(); float fDet = fabsf(fA00*fA11 - fA01*fA01); float u = fA01*fB1-fA11*fB0; float v = fA01*fB0-fA00*fB1; @@ -28,31 +48,31 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co { if(fB0 < 0.0f) { - v = 0.0f; - if(-fB0>=fA00) { u = 1.0f; SqrDist = fA00+2.0f*fB0+fC; } +// v = 0.0f; + if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } else { u = -fB0/fA00; SqrDist = fB0*u+fC; } } else { - u = 0.0f; - if(fB1>=0.0f) { v = 0.0f; SqrDist = fC; } - else if(-fB1>=fA11) { v = 1.0f; SqrDist = fA11+2.0f*fB1+fC; } +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } else { v = -fB1/fA11; SqrDist = fB1*v+fC; } } } else // region 3 { - u = 0.0f; - if(fB1>=0.0f) { v = 0.0f; SqrDist = fC; } - else if(-fB1>=fA11) { v = 1.0f; SqrDist = fA11+2.0f*fB1+fC; } +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } else { v = -fB1/fA11; SqrDist = fB1*v+fC; } } } else if(v < 0.0f) // region 5 { - v = 0.0f; - if(fB0>=0.0f) { u = 0.0f; SqrDist = fC; } - else if(-fB0>=fA00) { u = 1.0f; SqrDist = fA00+2.0f*fB0+fC; } +// v = 0.0f; + if(fB0>=0.0f) { /*u = 0.0f;*/ SqrDist = fC; } + else if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } else { u = -fB0/fA00; SqrDist = fB0*u+fC; } } else // region 0 @@ -60,8 +80,8 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co // minimum at interior point if(fDet==0.0f) { - u = 0.0f; - v = 0.0f; +// u = 0.0f; +// v = 0.0f; SqrDist = MAX_FLOAT; } else @@ -87,8 +107,8 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) { - u = 1.0f; - v = 0.0f; +// u = 1.0f; +// v = 0.0f; SqrDist = fA00+2.0f*fB0+fC; } else @@ -100,9 +120,9 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co } else { - u = 0.0f; - if(fTmp1 <= 0.0f) { v = 1.0f; SqrDist = fA11+2.0f*fB1+fC; } - else if(fB1 >= 0.0f) { v = 0.0f; SqrDist = fC; } +// u = 0.0f; + if(fTmp1 <= 0.0f) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else if(fB1 >= 0.0f) { /*v = 0.0f;*/ SqrDist = fC; } else { v = -fB1/fA11; SqrDist = fB1*v+fC; } } } @@ -116,8 +136,8 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) { - v = 1.0f; - u = 0.0f; +// v = 1.0f; +// u = 0.0f; SqrDist = fA11+2.0f*fB1+fC; } else @@ -129,9 +149,9 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co } else { - v = 0.0f; - if(fTmp1 <= 0.0f) { u = 1.0f; SqrDist = fA00+2.0f*fB0+fC; } - else if(fB0 >= 0.0f) { u = 0.0f; SqrDist = fC; } +// v = 0.0f; + if(fTmp1 <= 0.0f) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else if(fB0 >= 0.0f) { /*u = 0.0f;*/ SqrDist = fC; } else { u = -fB0/fA00; SqrDist = fB0*u+fC; } } } @@ -140,8 +160,8 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co fNumer = fA11 + fB1 - fA01 - fB0; if(fNumer <= 0.0f) { - u = 0.0f; - v = 1.0f; +// u = 0.0f; +// v = 1.0f; SqrDist = fA11+2.0f*fB1+fC; } else @@ -149,8 +169,8 @@ BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, co fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) { - u = 1.0f; - v = 0.0f; +// u = 1.0f; +// v = 0.0f; SqrDist = fA00+2.0f*fB0+fC; } else diff --git a/OPC_SweepAndPrune.cpp b/OPC_SweepAndPrune.cpp new file mode 100644 index 0000000..b593e8b --- /dev/null +++ b/OPC_SweepAndPrune.cpp @@ -0,0 +1,664 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide) + * \file OPC_SweepAndPrune.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +inline_ void Sort(udword& id0, udword& id1) +{ + if(id0>id1) Swap(id0, id1); +} + + class Opcode::SAP_Element + { + public: + inline_ SAP_Element() {} + inline_ SAP_Element(udword id, SAP_Element* next) : mID(id), mNext(next) {} + inline_ ~SAP_Element() {} + + udword mID; + SAP_Element* mNext; + }; + + class Opcode::SAP_Box + { + public: + SAP_EndPoint* Min[3]; + SAP_EndPoint* Max[3]; + }; + + class Opcode::SAP_EndPoint + { + public: + float Value; // Min or Max value + SAP_EndPoint* Previous; // Previous EndPoint whose Value is smaller than ours (or null) + SAP_EndPoint* Next; // Next EndPoint whose Value is greater than ours (or null) + udword Data; // Parent box ID *2 | MinMax flag + + inline_ void SetData(udword box_id, BOOL is_max) { Data = (box_id<<1)|is_max; } + inline_ BOOL IsMax() const { return Data & 1; } + inline_ udword GetBoxID() const { return Data>>1; } + + inline_ void InsertAfter(SAP_EndPoint* element) + { + if(this!=element && this!=element->Next) + { + // Remove + if(Previous) Previous->Next = Next; + if(Next) Next->Previous = Previous; + + // Insert + Next = element->Next; + if(Next) Next->Previous = this; + + element->Next = this; + Previous = element; + } + } + + inline_ void InsertBefore(SAP_EndPoint* element) + { + if(this!=element && this!=element->Previous) + { + // Remove + if(Previous) Previous->Next = Next; + if(Next) Next->Previous = Previous; + + // Insert + Previous = element->Previous; + element->Previous = this; + + Next = element; + if(Previous) Previous->Next = this; + } + } + }; + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_PairData::SAP_PairData() : + mNbElements (0), + mNbUsedElements (0), + mElementPool (null), + mFirstFree (null), + mNbObjects (0), + mArray (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_PairData::~SAP_PairData() +{ + Release(); +} + +void SAP_PairData::Release() +{ + mNbElements = 0; + mNbUsedElements = 0; + mNbObjects = 0; + DELETEARRAY(mElementPool); + DELETEARRAY(mArray); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes. + * \param nb_objects [in] + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SAP_PairData::Init(udword nb_objects) +{ + // Make sure everything has been released + Release(); + if(!nb_objects) return false; + + mArray = new SAP_Element*[nb_objects]; + CHECKALLOC(mArray); + ZeroMemory(mArray, nb_objects*sizeof(SAP_Element*)); + mNbObjects = nb_objects; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Remaps a pointer when pool gets resized. + * \param element [in/out] remapped element + * \param delta [in] offset in bytes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void Remap(SAP_Element*& element, udword delta) +{ + if(element) element = (SAP_Element*)(udword(element) + delta); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets a free element in the pool. + * \param id [in] element id + * \param next [in] next element + * \param remap [out] possible remapping offset + * \return the new element + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_Element* SAP_PairData::GetFreeElem(udword id, SAP_Element* next, udword* remap) +{ + if(remap) *remap = 0; + + SAP_Element* FreeElem; + if(mFirstFree) + { + // Recycle + FreeElem = mFirstFree; + mFirstFree = mFirstFree->mNext; // First free = next free (or null) + } + else + { + if(mNbUsedElements==mNbElements) + { + // Resize + mNbElements = mNbElements ? (mNbElements<<1) : 2; + + SAP_Element* NewElems = new SAP_Element[mNbElements]; + + if(mNbUsedElements) CopyMemory(NewElems, mElementPool, mNbUsedElements*sizeof(SAP_Element)); + + // Remap everything + { + udword Delta = udword(NewElems) - udword(mElementPool); + + for(udword i=0;imID = id; + FreeElem->mNext = next; + + return FreeElem; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Frees an element of the pool. + * \param elem [in] element to free/recycle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void SAP_PairData::FreeElem(SAP_Element* elem) +{ + elem->mNext = mFirstFree; // Next free + mFirstFree = elem; +} + +// Add a pair to the set. +void SAP_PairData::AddPair(udword id1, udword id2) +{ + // Order the ids + Sort(id1, id2); + + ASSERT(id1=mNbObjects) return; + + // Select the right list from "mArray". + SAP_Element* Current = mArray[id1]; + + if(!Current) + { + // Empty slot => create new element + mArray[id1] = GetFreeElem(id2, null); + } + else if(Current->mID>id2) + { + // The list is not empty but all elements are greater than id2 => insert id2 in the front. + mArray[id1] = GetFreeElem(id2, mArray[id1]); + } + else + { + // Else find the correct location in the sorted list (ascending order) and insert id2 there. + while(Current->mNext) + { + if(Current->mNext->mID > id2) break; + + Current = Current->mNext; + } + + if(Current->mID==id2) return; // The pair already exists + +// Current->mNext = GetFreeElem(id2, Current->mNext); + udword Delta; + SAP_Element* E = GetFreeElem(id2, Current->mNext, &Delta); + if(Delta) Remap(Current, Delta); + Current->mNext = E; + } +} + +// Delete a pair from the set. +void SAP_PairData::RemovePair(udword id1, udword id2) +{ + // Order the ids. + Sort(id1, id2); + + // Exit if the pair doesn't exist in the set + if(id1>=mNbObjects) return; + + // Otherwise, select the correct list. + SAP_Element* Current = mArray[id1]; + + // If this list is empty, the pair doesn't exist. + if(!Current) return; + + // Otherwise, if id2 is the first element, delete it. + if(Current->mID==id2) + { + mArray[id1] = Current->mNext; + FreeElem(Current); + } + else + { + // If id2 is not the first element, start traversing the sorted list. + while(Current->mNext) + { + // If we have moved too far away without hitting id2, then the pair doesn't exist + if(Current->mNext->mID > id2) return; + + // Otherwise, delete id2. + if(Current->mNext->mID == id2) + { + SAP_Element* Temp = Current->mNext; + Current->mNext = Temp->mNext; + FreeElem(Temp); + return; + } + Current = Current->mNext; + } + } +} + +void SAP_PairData::DumpPairs(Pairs& pairs) const +{ + // ### Ugly and slow + for(udword i=0;imIDmID); + Current = Current->mNext; + } + } +} + +void SAP_PairData::DumpPairs(PairCallback callback, void* user_data) const +{ + if(!callback) return; + + // ### Ugly and slow + for(udword i=0;imIDmID, user_data)) return; + Current = Current->mNext; + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SweepAndPrune::SweepAndPrune() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SweepAndPrune::~SweepAndPrune() +{ +} + +void SweepAndPrune::GetPairs(Pairs& pairs) const +{ + mPairs.DumpPairs(pairs); +} + +void SweepAndPrune::GetPairs(PairCallback callback, void* user_data) const +{ + mPairs.DumpPairs(callback, user_data); +} + +bool SweepAndPrune::Init(udword nb_objects, const AABB** boxes) +{ + // 1) Create sorted lists + mNbObjects = nb_objects; + + mBoxes = new SAP_Box[nb_objects]; +// for(udword i=0;iGetMin(Axis); + Data[i*2+1] = boxes[i]->GetMax(Axis); + } + RadixSort RS; + const udword* Sorted = RS.Sort(Data, nb_objects*2).GetRanks(); + + SAP_EndPoint* PreviousEndPoint = null; + + for(udword i=0;i>1; + + ASSERT(BoxIndexValue = SortedCoord; +// CurrentEndPoint->IsMax = SortedIndex&1; // ### could be implicit ? +// CurrentEndPoint->ID = BoxIndex; // ### could be implicit ? + CurrentEndPoint->SetData(BoxIndex, SortedIndex&1); // ### could be implicit ? + CurrentEndPoint->Previous = PreviousEndPoint; + CurrentEndPoint->Next = null; + if(PreviousEndPoint) PreviousEndPoint->Next = CurrentEndPoint; + + if(CurrentEndPoint->IsMax()) mBoxes[BoxIndex].Max[Axis] = CurrentEndPoint; + else mBoxes[BoxIndex].Min[Axis] = CurrentEndPoint; + + PreviousEndPoint = CurrentEndPoint; + } + } + + DELETEARRAY(Data); + + CheckListsIntegrity(); + + // 2) Quickly find starting pairs + + mPairs.Init(nb_objects); + + { + Pairs P; + CompleteBoxPruning(nb_objects, boxes, P, Axes(AXES_XZY)); + for(udword i=0;iid0; + udword id1 = PP->id1; + + if(id0!=id1 && boxes[id0]->Intersect(*boxes[id1])) + { + mPairs.AddPair(id0, id1); + } + else ASSERT(0); + } + } + + return true; +} + +bool SweepAndPrune::CheckListsIntegrity() +{ + for(udword Axis=0;Axis<3;Axis++) + { + // Find list head + SAP_EndPoint* Current = mList[Axis]; + while(Current->Previous) Current = Current->Previous; + + udword Nb = 0; + + SAP_EndPoint* Previous = null; + while(Current) + { + Nb++; + + if(Previous) + { + ASSERT(Previous->Value <= Current->Value); + if(Previous->Value > Current->Value) return false; + } + + ASSERT(Current->Previous==Previous); + if(Current->Previous!=Previous) return false; + + Previous = Current; + Current = Current->Next; + } + + ASSERT(Nb==mNbObjects*2); + } + return true; +} + +inline_ BOOL Intersect(const AABB& a, const SAP_Box& b) +{ + if(b.Max[0]->Value < a.GetMin(0) || a.GetMax(0) < b.Min[0]->Value + || b.Max[1]->Value < a.GetMin(1) || a.GetMax(1) < b.Min[1]->Value + || b.Max[2]->Value < a.GetMin(2) || a.GetMax(2) < b.Min[2]->Value) return FALSE; + + return TRUE; +} + + + +bool SweepAndPrune::UpdateObject(udword i, const AABB& box) +{ + for(udword Axis=0;Axis<3;Axis++) + { +// udword Base = (udword)&mList[Axis][0]; + + // Update min + { + SAP_EndPoint* const CurrentMin = mBoxes[i].Min[Axis]; + ASSERT(!CurrentMin->IsMax()); + + const float Limit = box.GetMin(Axis); + if(Limit == CurrentMin->Value) + { + } + else if(Limit < CurrentMin->Value) + { + CurrentMin->Value = Limit; + + // Min is moving left: + SAP_EndPoint* NewPos = CurrentMin; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Previous) && tmp->Value > Limit) + { + NewPos = tmp; + + if(NewPos->IsMax()) + { + // Our min passed a max => start overlap + //udword SortedIndex = (udword(CurrentMin) - Base)/sizeof(NS_EndPoint); + const udword id0 = CurrentMin->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1); + } + } + + CurrentMin->InsertBefore(NewPos); + } + else// if(Limit > CurrentMin->Value) + { + CurrentMin->Value = Limit; + + // Min is moving right: + SAP_EndPoint* NewPos = CurrentMin; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Next) && tmp->Value < Limit) + { + NewPos = tmp; + + if(NewPos->IsMax()) + { + // Our min passed a max => stop overlap + const udword id0 = CurrentMin->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1) mPairs.RemovePair(id0, id1); + } + } + + CurrentMin->InsertAfter(NewPos); + } + } + + // Update max + { + SAP_EndPoint* const CurrentMax = mBoxes[i].Max[Axis]; + ASSERT(CurrentMax->IsMax()); + + const float Limit = box.GetMax(Axis); + if(Limit == CurrentMax->Value) + { + } + else if(Limit > CurrentMax->Value) + { + CurrentMax->Value = Limit; + + // Max is moving right: + SAP_EndPoint* NewPos = CurrentMax; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Next) && tmp->Value < Limit) + { + NewPos = tmp; + + if(!NewPos->IsMax()) + { + // Our max passed a min => start overlap + const udword id0 = CurrentMax->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1); + } + } + + CurrentMax->InsertAfter(NewPos); + } + else// if(Limit < CurrentMax->Value) + { + CurrentMax->Value = Limit; + + // Max is moving left: + SAP_EndPoint* NewPos = CurrentMax; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Previous) && tmp->Value > Limit) + { + NewPos = tmp; + + if(!NewPos->IsMax()) + { + // Our max passed a min => stop overlap + const udword id0 = CurrentMax->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1) mPairs.RemovePair(id0, id1); + } + } + + CurrentMax->InsertBefore(NewPos); + } + } + } + + return true; +} diff --git a/OPC_SweepAndPrune.h b/OPC_SweepAndPrune.h new file mode 100644 index 0000000..5cf7956 --- /dev/null +++ b/OPC_SweepAndPrune.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide) + * \file OPC_SweepAndPrune.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SWEEPANDPRUNE_H__ +#define __OPC_SWEEPANDPRUNE_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE for each colliding pairs. + * \param id0 [in] id of colliding object + * \param id1 [in] id of colliding object + * \param user_data [in] user-defined data + * \return TRUE to continue enumeration + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef BOOL (*PairCallback) (udword id0, udword id1, void* user_data); + + class SAP_Element; + class SAP_EndPoint; + class SAP_Box; + + class OPCODE_API SAP_PairData + { + public: + SAP_PairData(); + ~SAP_PairData(); + + bool Init(udword nb_objects); + + void AddPair(udword id1, udword id2); + void RemovePair(udword id1, udword id2); + + void DumpPairs(Pairs& pairs) const; + void DumpPairs(PairCallback callback, void* user_data) const; + private: + udword mNbElements; //!< Total number of elements in the pool + udword mNbUsedElements; //!< Number of used elements + SAP_Element* mElementPool; //!< Array of mNbElements elements + SAP_Element* mFirstFree; //!< First free element in the pool + + udword mNbObjects; //!< Max number of objects we can handle + SAP_Element** mArray; //!< Pointers to pool + // Internal methods + SAP_Element* GetFreeElem(udword id, SAP_Element* next, udword* remap=null); + inline_ void FreeElem(SAP_Element* elem); + void Release(); + }; + + class OPCODE_API SweepAndPrune + { + public: + SweepAndPrune(); + ~SweepAndPrune(); + + bool Init(udword nb_objects, const AABB** boxes); + bool UpdateObject(udword i, const AABB& box); + + void GetPairs(Pairs& pairs) const; + void GetPairs(PairCallback callback, void* user_data) const; + private: + SAP_PairData mPairs; + + udword mNbObjects; + SAP_Box* mBoxes; + SAP_EndPoint* mList[3]; + // Internal methods + bool CheckListsIntegrity(); + }; + +#endif //__OPC_SWEEPANDPRUNE_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_TreeBuilders.cpp b/OPC_TreeBuilders.cpp similarity index 60% rename from OpcodeDistrib/OPC_TreeBuilders.cpp rename to OPC_TreeBuilders.cpp index c49a977..73e89c7 100644 --- a/OpcodeDistrib/OPC_TreeBuilders.cpp +++ b/OPC_TreeBuilders.cpp @@ -15,13 +15,24 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of vertices. + * + * \class AABBTreeOfVerticesBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * A builder for AABB-trees of AABBs. * * \class AABBTreeOfAABBsBuilder * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,7 +43,7 @@ * * \class AABBTreeOfTrianglesBuilder * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,15 +69,14 @@ bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const udword* primitives, udword n if(!primitives || !nb_prims) return false; // Initialize global box - global_box = mAABBList[primitives[0]]; + global_box = mAABBArray[primitives[0]]; // Loop through boxes for(udword i=1;iGetTriangle(VP, *primitives++); // Update global box - Min.Min(p0).Min(p1).Min(p2); - Max.Max(p0).Max(p1).Max(p2); + Min.Min(*VP.Vertex[0]).Min(*VP.Vertex[1]).Min(*VP.Vertex[2]); + Max.Max(*VP.Vertex[0]).Max(*VP.Vertex[1]).Max(*VP.Vertex[2]); } global_box.SetMinMax(Min, Max); return true; @@ -135,9 +144,17 @@ float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) c return Center[axis];*/ // Compute correct component from center of triangle - return (mVerts[mTriList[index].mVRef[0]][axis] - +mVerts[mTriList[index].mVRef[1]][axis] - +mVerts[mTriList[index].mVRef[2]][axis])*INV3; +// return (mVerts[mTriList[index].mVRef[0]][axis] +// +mVerts[mTriList[index].mVRef[1]][axis] +// +mVerts[mTriList[index].mVRef[2]][axis])*INV3; + + VertexPointers VP; + mIMesh->GetTriangle(VP, index); + + // Compute correct component from center of triangle + return ((*VP.Vertex[0])[axis] + +(*VP.Vertex[1])[axis] + +(*VP.Vertex[2])[axis])*INV3; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -152,22 +169,87 @@ float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) c /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float AABBTreeOfTrianglesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const { - if(mRules&SPLIT_GEOMCENTER) + if(mSettings.mRules&SPLIT_GEOM_CENTER) { // Loop through triangles float SplitValue = 0.0f; + VertexPointers VP; for(udword i=0;iGetTriangle(VP, primitives[i]); // Update split value - SplitValue += p0[axis]; - SplitValue += p1[axis]; - SplitValue += p2[axis]; + SplitValue += (*VP.Vertex[0])[axis]; + SplitValue += (*VP.Vertex[1])[axis]; + SplitValue += (*VP.Vertex[2])[axis]; } return SplitValue / float(nb_prims*3); } else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis); } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the AABB of a set of primitives. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [out] global AABB enclosing the set of input primitives + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const +{ + // Checkings + if(!primitives || !nb_prims) return false; + + // Initialize global box + global_box.SetEmpty(); + + // Loop through vertices + for(udword i=0;iGetMeshInterface(), cache.Model1->GetMeshInterface())) return false; + // Simple double-dispatch bool Status; if(!cache.Model0->HasLeafNodes()) @@ -281,8 +268,9 @@ void AABBTreeCollider::InitQuery(const Matrix4x4* world0, const Matrix4x4* world /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to take advantage of temporal coherence. + * Takes advantage of temporal coherence. * \param cache [in] cache for a pair of previously colliding primitives + * \return true if we can return immediately * \warning only works for "First Contact" mode */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -321,15 +309,6 @@ bool AABBTreeCollider::CheckTemporalCoherence(Pair* cache) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) { - // Checkings - if(!tree0 || !tree1) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback0 || !mObjCallback1) return false; -#else - if(!mFaces0 || !mVerts0) return false; - if(!mFaces1 || !mVerts1) return false; -#endif - // Init collision query InitQuery(world0, world1); @@ -358,15 +337,6 @@ bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisi /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) { - // Checkings - if(!tree0 || !tree1) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback0 || !mObjCallback1) return false; -#else - if(!mFaces0 || !mVerts0) return false; - if(!mFaces1 || !mVerts1) return false; -#endif - // Init collision query InitQuery(world0, world1); @@ -395,15 +365,6 @@ bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) { - // Checkings - if(!tree0 || !tree1) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback0 || !mObjCallback1) return false; -#else - if(!mFaces0 || !mVerts0) return false; - if(!mFaces1 || !mVerts1) return false; -#endif - // Init collision query InitQuery(world0, world1); @@ -447,15 +408,6 @@ bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantiz /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool AABBTreeCollider::Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) { - // Checkings - if(!tree0 || !tree1) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback0 || !mObjCallback1) return false; -#else - if(!mFaces0 || !mVerts0) return false; - if(!mFaces1 || !mVerts1) return false; -#endif - // Init collision query InitQuery(world0, world1); @@ -522,7 +474,6 @@ void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollision * \param b1 [in] collision node from second tree */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1) { // Perform BV-BV overlap test @@ -579,19 +530,8 @@ void AABBTreeCollider::PrimTest(udword id0, udword id1) // Request vertices from the app VertexPointers VP0; VertexPointers VP1; -#ifdef OPC_USE_CALLBACKS - (mObjCallback0)(id0, VP0, mUserData0); - (mObjCallback1)(id1, VP1, mUserData1); -#else - const IndexedTriangle* Tri0 = &mFaces0[id0]; - VP0.Vertex[0] = &mVerts0[Tri0->mVRef[0]]; - VP0.Vertex[1] = &mVerts0[Tri0->mVRef[1]]; - VP0.Vertex[2] = &mVerts0[Tri0->mVRef[2]]; - const IndexedTriangle* Tri1 = &mFaces1[id1]; - VP1.Vertex[0] = &mVerts1[Tri1->mVRef[0]]; - VP1.Vertex[1] = &mVerts1[Tri1->mVRef[1]]; - VP1.Vertex[2] = &mVerts1[Tri1->mVRef[2]]; -#endif + mIMesh0->GetTriangle(VP0, id0); + mIMesh1->GetTriangle(VP1, id1); // Transform from space 1 to space 0 Point u0,u1,u2; @@ -619,14 +559,8 @@ inline_ void AABBTreeCollider::PrimTestTriIndex(udword id1) { // Request vertices from the app VertexPointers VP; -#ifdef OPC_USE_CALLBACKS - (mObjCallback1)(id1, VP, mUserData1); -#else - const IndexedTriangle* Tri1 = &mFaces1[id1]; - VP.Vertex[0] = &mVerts1[Tri1->mVRef[0]]; - VP.Vertex[1] = &mVerts1[Tri1->mVRef[1]]; - VP.Vertex[2] = &mVerts1[Tri1->mVRef[2]]; -#endif + mIMesh1->GetTriangle(VP, id1); + // Perform triangle-triangle overlap test if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) { @@ -647,14 +581,7 @@ inline_ void AABBTreeCollider::PrimTestIndexTri(udword id0) { // Request vertices from the app VertexPointers VP; -#ifdef OPC_USE_CALLBACKS - (mObjCallback0)(id0, VP, mUserData0); -#else - const IndexedTriangle* Tri0 = &mFaces0[id0]; - VP.Vertex[0] = &mVerts0[Tri0->mVRef[0]]; - VP.Vertex[1] = &mVerts0[Tri0->mVRef[1]]; - VP.Vertex[2] = &mVerts0[Tri0->mVRef[2]]; -#endif + mIMesh0->GetTriangle(VP, id0); // Perform triangle-triangle overlap test if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) @@ -678,13 +605,13 @@ void AABBTreeCollider::_CollideTriBox(const AABBNoLeafNode* b) if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; // Keep same triangle, deal with first child - if(b->HasLeaf()) PrimTestTriIndex(b->GetPrimitive()); + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; // Keep same triangle, deal with second child - if(b->HasLeaf2()) PrimTestTriIndex(b->GetPrimitive2()); + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } @@ -700,36 +627,25 @@ void AABBTreeCollider::_CollideBoxTri(const AABBNoLeafNode* b) if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; // Keep same triangle, deal with first child - if(b->HasLeaf()) PrimTestIndexTri(b->GetPrimitive()); + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); else _CollideBoxTri(b->GetPos()); if(ContactFound()) return; // Keep same triangle, deal with second child - if(b->HasLeaf2()) PrimTestIndexTri(b->GetPrimitive2()); + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); else _CollideBoxTri(b->GetNeg()); } //! Request triangle vertices from the app and transform them -#ifdef OPC_USE_CALLBACKS - #define FETCH_LEAF(primindex, callback, user_data, rot, trans) \ - mLeafIndex = primindex; \ - /* Request vertices from the app */ \ - VertexPointers VP; (callback)(primindex, VP, user_data); \ - /* Transform them in a common space */ \ - TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \ - TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \ - TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans); -#else - #define FETCH_LEAF(primindex, faces, verts, rot, trans) \ - mLeafIndex = primindex; \ - /* Direct access to vertices */ \ - const IndexedTriangle* T = &faces[primindex]; \ - /* Transform them in a common space */ \ - TransformPoint(mLeafVerts[0], verts[T->mVRef[0]], rot, trans); \ - TransformPoint(mLeafVerts[1], verts[T->mVRef[1]], rot, trans); \ - TransformPoint(mLeafVerts[2], verts[T->mVRef[2]], rot, trans); -#endif +#define FETCH_LEAF(prim_index, imesh, rot, trans) \ + mLeafIndex = prim_index; \ + /* Request vertices from the app */ \ + VertexPointers VP; imesh->GetTriangle(VP, prim_index); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -744,46 +660,37 @@ void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b if(!BoxBoxOverlap(a->mAABB.mExtents, a->mAABB.mCenter, b->mAABB.mExtents, b->mAABB.mCenter)) return; // Catch leaf status - BOOL BHasLeaf = b->HasLeaf(); - BOOL BHasLeaf2 = b->HasLeaf2(); + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); - if(a->HasLeaf()) + if(a->HasPosLeaf()) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(a->GetPrimitive(), mObjCallback0, mUserData0, mR0to1, mT0to1) -#else - FETCH_LEAF(a->GetPrimitive(), mFaces0, mVerts0, mR0to1, mT0to1) -#endif - if(BHasLeaf) PrimTestTriIndex(b->GetPrimitive()); + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) PrimTestTriIndex(b->GetPrimitive2()); + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } else { - if(BHasLeaf) + if(BHasPosLeaf) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetPos()); } else _Collide(a->GetPos(), b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) + if(BHasNegLeaf) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive2(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive2(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetPos()); } else _Collide(a->GetPos(), b->GetNeg()); @@ -791,45 +698,36 @@ void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b if(ContactFound()) return; - if(a->HasLeaf2()) + if(a->HasNegLeaf()) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(a->GetPrimitive2(), mObjCallback0, mUserData0, mR0to1, mT0to1) -#else - FETCH_LEAF(a->GetPrimitive2(), mFaces0, mVerts0, mR0to1, mT0to1) -#endif - if(BHasLeaf) PrimTestTriIndex(b->GetPrimitive()); + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) PrimTestTriIndex(b->GetPrimitive2()); + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } else { - if(BHasLeaf) + if(BHasPosLeaf) { // ### That leaf has possibly already been fetched -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetNeg()); } else _Collide(a->GetNeg(), b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) + if(BHasNegLeaf) { // ### That leaf has possibly already been fetched -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive2(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive2(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetNeg()); } else _Collide(a->GetNeg(), b->GetNeg()); @@ -913,12 +811,12 @@ void AABBTreeCollider::_CollideTriBox(const AABBQuantizedNoLeafNode* b) // Perform triangle-box overlap test if(!TriBoxOverlap(Pb, eb)) return; - if(b->HasLeaf()) PrimTestTriIndex(b->GetPrimitive()); + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; - if(b->HasLeaf2()) PrimTestTriIndex(b->GetPrimitive2()); + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } @@ -939,12 +837,12 @@ void AABBTreeCollider::_CollideBoxTri(const AABBQuantizedNoLeafNode* b) // Perform triangle-box overlap test if(!TriBoxOverlap(Pa, ea)) return; - if(b->HasLeaf()) PrimTestIndexTri(b->GetPrimitive()); + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); else _CollideBoxTri(b->GetPos()); if(ContactFound()) return; - if(b->HasLeaf2()) PrimTestIndexTri(b->GetPrimitive2()); + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); else _CollideBoxTri(b->GetNeg()); } @@ -970,46 +868,37 @@ void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuan if(!BoxBoxOverlap(ea, Pa, eb, Pb)) return; // Catch leaf status - BOOL BHasLeaf = b->HasLeaf(); - BOOL BHasLeaf2 = b->HasLeaf2(); + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); - if(a->HasLeaf()) + if(a->HasPosLeaf()) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(a->GetPrimitive(), mObjCallback0, mUserData0, mR0to1, mT0to1) -#else - FETCH_LEAF(a->GetPrimitive(), mFaces0, mVerts0, mR0to1, mT0to1) -#endif - if(BHasLeaf) PrimTestTriIndex(b->GetPrimitive()); + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) PrimTestTriIndex(b->GetPrimitive2()); + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } else { - if(BHasLeaf) + if(BHasPosLeaf) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetPos()); } else _Collide(a->GetPos(), b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) + if(BHasNegLeaf) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive2(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive2(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetPos()); } else _Collide(a->GetPos(), b->GetNeg()); @@ -1017,45 +906,36 @@ void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuan if(ContactFound()) return; - if(a->HasLeaf2()) + if(a->HasNegLeaf()) { -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(a->GetPrimitive2(), mObjCallback0, mUserData0, mR0to1, mT0to1) -#else - FETCH_LEAF(a->GetPrimitive2(), mFaces0, mVerts0, mR0to1, mT0to1) -#endif - if(BHasLeaf) PrimTestTriIndex(b->GetPrimitive()); + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); else _CollideTriBox(b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) PrimTestTriIndex(b->GetPrimitive2()); + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); else _CollideTriBox(b->GetNeg()); } else { - if(BHasLeaf) + if(BHasPosLeaf) { // ### That leaf has possibly already been fetched -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetNeg()); } else _Collide(a->GetNeg(), b->GetPos()); if(ContactFound()) return; - if(BHasLeaf2) + if(BHasNegLeaf) { // ### That leaf has possibly already been fetched -#ifdef OPC_USE_CALLBACKS - FETCH_LEAF(b->GetPrimitive2(), mObjCallback1, mUserData1, mR1to0, mT1to0) -#else - FETCH_LEAF(b->GetPrimitive2(), mFaces1, mVerts1, mR1to0, mT1to0) -#endif + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + _CollideBoxTri(a->GetNeg()); } else _Collide(a->GetNeg(), b->GetNeg()); diff --git a/OpcodeDistrib/OPC_TreeCollider.h b/OPC_TreeCollider.h similarity index 73% rename from OpcodeDistrib/OPC_TreeCollider.h rename to OPC_TreeCollider.h index 20a0f02..bb8d1b6 100644 --- a/OpcodeDistrib/OPC_TreeCollider.h +++ b/OPC_TreeCollider.h @@ -20,6 +20,52 @@ #ifndef __OPC_TREECOLLIDER_H__ #define __OPC_TREECOLLIDER_H__ + //! This structure holds cached information used by the algorithm. + //! Two model pointers and two colliding primitives are cached. Model pointers are assigned + //! to their respective meshes, and the pair of colliding primitives is used for temporal + //! coherence. That is, in case temporal coherence is enabled, those two primitives are + //! tested for overlap before everything else. If they still collide, we're done before + //! even entering the recursive collision code. + struct OPCODE_API BVTCache : Pair + { + //! Constructor + inline_ BVTCache() + { + ResetCache(); + ResetCountDown(); + } + + void ResetCache() + { + Model0 = null; + Model1 = null; + id0 = 0; + id1 = 1; +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + HullTest = true; + SepVector.pid = 0; + SepVector.qid = 0; + SepVector.SV = Point(1.0f, 0.0f, 0.0f); +#endif // __MESHMERIZER_H__ + } + + inline_ void ResetCountDown() + { +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + CountDown = 50; +#endif // __MESHMERIZER_H__ + } + + const Model* Model0; //!< Model for first object + const Model* Model1; //!< Model for second object + +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + SVCache SepVector; + udword CountDown; + bool HullTest; +#endif // __MESHMERIZER_H__ + }; + class OPCODE_API AABBTreeCollider : public Collider { public: @@ -123,47 +169,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ const Pair* GetPairs() const { return (const Pair*)mPairs.GetEntries(); } -#ifdef OPC_USE_CALLBACKS - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Callback control: a method to setup callback 0. Must provide triangle-vertices for a given triangle index. - * \param callback [in] user-defined callback - * \param data [in] user-defined data - * \see SetCallback1(OPC_CALLBACK callback, udword data) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetCallback0(OPC_CALLBACK callback, udword data) { mObjCallback0 = callback; mUserData0 = data; } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Callback control: a method to setup callback 1. Must provide triangle-vertices for a given triangle index. - * \param callback [in] user-defined callback - * \param data [in] user-defined data - * \see SetCallback0(OPC_CALLBACK callback, udword data) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetCallback1(OPC_CALLBACK callback, udword data) { mObjCallback1 = callback; mUserData1 = data; } -#else - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Pointers control: a method to setup object0 pointers. Must provide access to faces and vertices for a given object. - * \param faces [in] pointer to faces - * \param verts [in] pointer to vertices - * \see SetPointers1(const Triangle* faces, const Point* verts) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetPointers0(const IndexedTriangle* faces, const Point* verts) { mFaces0 = faces; mVerts0 = verts; } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Pointers control: a method to setup object1 pointers. Must provide access to faces and vertices for a given object. - * \param faces [in] pointer to faces - * \param verts [in] pointer to vertices - * \see SetPointers0(const Triangle* faces, const Point* verts) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetPointers1(const IndexedTriangle* faces, const Point* verts) { mFaces1 = faces; mVerts1 = verts; } -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. @@ -175,19 +180,9 @@ protected: // Colliding pairs Container mPairs; //!< Pairs of colliding primitives -#ifdef OPC_USE_CALLBACKS - // User callback - udword mUserData0; //!< User-defined data sent to callbacks - udword mUserData1; //!< User-defined data sent to callbacks - OPC_CALLBACK mObjCallback0; //!< Callback for object 0 - OPC_CALLBACK mObjCallback1; //!< Callback for object 1 -#else - // User pointers - const IndexedTriangle* mFaces0; //!< User-defined faces - const IndexedTriangle* mFaces1; //!< User-defined faces - const Point* mVerts0; //!< User-defined vertices - const Point* mVerts1; //!< User-defined vertices -#endif + // User mesh interfaces + const MeshInterface* mIMesh0; //!< User-defined mesh interface for object0 + const MeshInterface* mIMesh1; //!< User-defined mesh interface for object1 // Stats udword mNbBVBVTests; //!< Number of BV-BV tests udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests @@ -234,6 +229,16 @@ // Init methods void InitQuery(const Matrix4x4* world0=null, const Matrix4x4* world1=null); bool CheckTemporalCoherence(Pair* cache); + + inline_ BOOL Setup(const MeshInterface* mi0, const MeshInterface* mi1) + { + mIMesh0 = mi0; + mIMesh1 = mi1; + + if(!mIMesh0 || !mIMesh1) return FALSE; + + return TRUE; + } }; #endif // __OPC_TREECOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_TriBoxOverlap.h b/OPC_TriBoxOverlap.h similarity index 100% rename from OpcodeDistrib/OPC_TriBoxOverlap.h rename to OPC_TriBoxOverlap.h diff --git a/OpcodeDistrib/OPC_TriTriOverlap.h b/OPC_TriTriOverlap.h similarity index 100% rename from OpcodeDistrib/OPC_TriTriOverlap.h rename to OPC_TriTriOverlap.h diff --git a/OpcodeDistrib/OPC_VolumeCollider.cpp b/OPC_VolumeCollider.cpp similarity index 81% rename from OpcodeDistrib/OPC_VolumeCollider.cpp rename to OPC_VolumeCollider.cpp index 3b96b35..70fc292 100644 --- a/OpcodeDistrib/OPC_VolumeCollider.cpp +++ b/OPC_VolumeCollider.cpp @@ -21,7 +21,7 @@ * * \class VolumeCollider * \author Pierre Terdiman - * \version 1.2 + * \version 1.3 * \date June, 2, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,13 +39,6 @@ using namespace Opcode; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// VolumeCollider::VolumeCollider() : mTouchedPrimitives (null), -#ifdef OPC_USE_CALLBACKS - mUserData (0), - mObjCallback (null), -#else - mFaces (null), - mVerts (null), -#endif mNbVolumeBVTests (0), mNbVolumePrimTests (0) { @@ -69,26 +62,21 @@ VolumeCollider::~VolumeCollider() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char* VolumeCollider::ValidateSettings() { -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return "Object callback must be defined! Call: SetCallback()."; -#else - if(!mFaces || !mVerts) return "Object pointers must be defined! Call: SetPointers()."; -#endif return null; } -// Pretty dumb way to dump - to do better +// Pretty dumb way to dump - to do better - one day... -#define IMPLEMENT_NOLEAFDUMP(type) \ -void VolumeCollider::_Dump(const type* node) \ -{ \ - if(node->HasLeaf()) mTouchedPrimitives->Add(node->GetPrimitive()); \ - else _Dump(node->GetPos()); \ - \ - if(ContactFound()) return; \ - \ - if(node->HasLeaf2()) mTouchedPrimitives->Add(node->GetPrimitive2()); \ - else _Dump(node->GetNeg()); \ +#define IMPLEMENT_NOLEAFDUMP(type) \ +void VolumeCollider::_Dump(const type* node) \ +{ \ + if(node->HasPosLeaf()) mTouchedPrimitives->Add(node->GetPosPrimitive()); \ + else _Dump(node->GetPos()); \ + \ + if(ContactFound()) return; \ + \ + if(node->HasNegLeaf()) mTouchedPrimitives->Add(node->GetNegPrimitive()); \ + else _Dump(node->GetNeg()); \ } #define IMPLEMENT_LEAFDUMP(type) \ diff --git a/OpcodeDistrib/OPC_VolumeCollider.h b/OPC_VolumeCollider.h similarity index 71% rename from OpcodeDistrib/OPC_VolumeCollider.h rename to OPC_VolumeCollider.h index 48ce685..05ea693 100644 --- a/OpcodeDistrib/OPC_VolumeCollider.h +++ b/OPC_VolumeCollider.h @@ -22,11 +22,11 @@ struct OPCODE_API VolumeCache { - VolumeCache() - { - } + VolumeCache() : Model(null) {} + ~VolumeCache() {} - Container TouchedPrimitives; //!< Indices of touched primitives + Container TouchedPrimitives; //!< Indices of touched primitives + const BaseModel* Model; //!< Owner }; class OPCODE_API VolumeCollider : public Collider @@ -43,7 +43,7 @@ * Gets the number of touched primitives after a collision query. * \see GetContactStatus() * \see GetTouchedPrimitives() - * \return the number of touched faces + * \return the number of touched primitives */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline_ udword GetNbTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetNbEntries() : 0; } @@ -56,7 +56,7 @@ * \return the list of touched primitives (primitive indices) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; } + inline_ const udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; } // Stats @@ -80,25 +80,6 @@ // Settings -#ifdef OPC_USE_CALLBACKS - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Callback control: a method to setup object callback. Must provide triangle-vertices for a given triangle index. - * \param callback [in] user-defined callback - * \param user_data [in] user-defined data - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetCallback(OPC_CALLBACK callback, udword user_data) { mObjCallback = callback; mUserData = user_data; } -#else - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Pointers control: a method to setup object pointers. Must provide access to faces and vertices for a given object. - * \param faces [in] pointer to faces - * \param verts [in] pointer to vertices - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void SetPointers(const IndexedTriangle* faces, const Point* verts) { mFaces = faces; mVerts = verts; } -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. @@ -110,15 +91,7 @@ protected: // Touched primitives Container* mTouchedPrimitives; //!< List of touched primitives -#ifdef OPC_USE_CALLBACKS - // User callback - udword mUserData; //!< User-defined data sent to callback - OPC_CALLBACK mObjCallback; //!< Object callback -#else - // User pointers - const IndexedTriangle* mFaces; //!< User-defined faces - const Point* mVerts; //!< User-defined vertices -#endif + // Dequantization coeffs Point mCenterCoeff; Point mExtentsCoeff; @@ -137,12 +110,29 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// override(Collider) inline_ void InitQuery() - { - // Reset stats & contact status - mNbVolumeBVTests = 0; - mNbVolumePrimTests = 0; - Collider::InitQuery(); - } + { + // Reset stats & contact status + mNbVolumeBVTests = 0; + mNbVolumePrimTests = 0; + Collider::InitQuery(); + } + + inline_ BOOL IsCacheValid(VolumeCache& cache) + { + // We're going to do a volume-vs-model query. + if(cache.Model!=mCurrentModel) + { + // Cached list was for another model so we can't keep it + // Keep track of new owner and reset cache + cache.Model = mCurrentModel; + return FALSE; + } + else + { + // Same models, no problem + return TRUE; + } + } }; #endif // __OPC_VOLUMECOLLIDER_H__ diff --git a/OpcodeDistrib/Opcode.cpp b/Opcode.cpp similarity index 85% rename from OpcodeDistrib/Opcode.cpp rename to Opcode.cpp index 21592f3..72d6b47 100644 --- a/OpcodeDistrib/Opcode.cpp +++ b/Opcode.cpp @@ -35,16 +35,31 @@ // Precompiled Header #include "Stdafx.h" +bool Opcode::InitOpcode() +{ + Log("// Initializing OPCODE\n\n"); +// LogAPIInfo(); + return true; +} + +void ReleasePruningSorters(); +bool Opcode::CloseOpcode() +{ + Log("// Closing OPCODE\n\n"); + + ReleasePruningSorters(); + + return true; +} + #ifdef ICE_MAIN -void ModuleAttach(udword hmod) +void ModuleAttach(HINSTANCE hinstance) { - Log("// Opening OPCODE\n\n"); } void ModuleDetach() { - Log("// Closing OPCODE\n\n"); } #endif \ No newline at end of file diff --git a/OpcodeDistrib/Opcode.dsp b/Opcode.dsp similarity index 63% rename from OpcodeDistrib/Opcode.dsp rename to Opcode.dsp index 41f4b5a..74d3b65 100644 --- a/OpcodeDistrib/Opcode.dsp +++ b/Opcode.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /G6 /MT /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /QIfist /c +# ADD CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /QIfist /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "NDEBUG" @@ -53,12 +53,8 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 /nologo /dll /machine:I386 /out:"Y:\dll\Opcode.dll" +# ADD LINK32 /nologo /dll /machine:I386 # SUBTRACT LINK32 /debug -# Begin Special Build Tool -SOURCE="$(InputPath)" -PostBuild_Cmds=xcopy release\Opcode.lib y:\lib /Q /Y -# End Special Build Tool !ELSEIF "$(CFG)" == "Opcode - Win32 Debug" @@ -74,7 +70,7 @@ PostBuild_Cmds=xcopy release\Opcode.lib y:\lib /Q /Y # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /G6 /MTd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /QIfist /c +# ADD CPP /nologo /MTd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /QIfist /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "_DEBUG" @@ -84,11 +80,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /dll /incremental:no /debug /machine:I386 /out:"Y:\dll\Opcode_D.dll" /pdbtype:sept -# Begin Special Build Tool -SOURCE="$(InputPath)" -PostBuild_Cmds=xcopy debug\Opcode_D.lib y:\lib /Q /Y -# End Special Build Tool +# ADD LINK32 /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug\Opcode_D.dll" /pdbtype:sept !ENDIF @@ -101,6 +93,26 @@ PostBuild_Cmds=xcopy debug\Opcode_D.lib y:\lib /Q /Y # PROP Default_Filter "" # Begin Source File +SOURCE=.\OPC_BaseModel.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_BaseModel.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_HybridModel.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_HybridModel.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_IceHook.h +# End Source File +# Begin Source File + SOURCE=.\OPC_Model.cpp # End Source File # Begin Source File @@ -132,7 +144,30 @@ SOURCE=.\StdAfx.h # Begin Group "Trees" # PROP Default_Filter "" -# Begin Group "Queries" +# Begin Group "Collision queries" + +# PROP Default_Filter "" +# Begin Group "Base colliders" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\OPC_Collider.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_Collider.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_VolumeCollider.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_VolumeCollider.h +# End Source File +# End Group +# Begin Group "Standard colliders" # PROP Default_Filter "" # Begin Source File @@ -145,11 +180,11 @@ SOURCE=.\OPC_AABBCollider.h # End Source File # Begin Source File -SOURCE=.\OPC_Collider.cpp +SOURCE=.\OPC_LSSCollider.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Collider.h +SOURCE=.\OPC_LSSCollider.h # End Source File # Begin Source File @@ -191,14 +226,7 @@ SOURCE=.\OPC_TreeCollider.cpp SOURCE=.\OPC_TreeCollider.h # End Source File -# Begin Source File - -SOURCE=.\OPC_VolumeCollider.cpp -# End Source File -# Begin Source File - -SOURCE=.\OPC_VolumeCollider.h -# End Source File +# End Group # End Group # Begin Source File @@ -210,15 +238,19 @@ SOURCE=.\OPC_AABBTree.h # End Source File # Begin Source File -SOURCE=.\OPC_BVTCache.h +SOURCE=.\OPC_Common.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Common.cpp +SOURCE=.\OPC_Common.h # End Source File # Begin Source File -SOURCE=.\OPC_Common.h +SOURCE=.\OPC_MeshInterface.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_MeshInterface.h # End Source File # Begin Source File @@ -246,6 +278,14 @@ SOURCE=.\OPC_BoxBoxOverlap.h # End Source File # Begin Source File +SOURCE=.\OPC_LSSAABBOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_LSSTriOverlap.h +# End Source File +# Begin Source File + SOURCE=.\OPC_PlanesAABBOverlap.h # End Source File # Begin Source File @@ -277,96 +317,196 @@ SOURCE=.\OPC_TriBoxOverlap.h SOURCE=.\OPC_TriTriOverlap.h # End Source File # End Group -# Begin Group "Ice code" +# Begin Group "SweepAndPrune" # PROP Default_Filter "" # Begin Source File -SOURCE=.\OPC_AABB.cpp +SOURCE=.\OPC_BoxPruning.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_BoxPruning.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_SweepAndPrune.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_SweepAndPrune.h +# End Source File +# End Group +# Begin Group "Usages" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\OPC_Picking.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_Picking.h +# End Source File +# End Group +# Begin Group "Ice" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Ice\IceAABB.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceAABB.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceAxes.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceBoundingSphere.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceContainer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceContainer.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceFPU.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceHPoint.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceHPoint.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceIndexedTriangle.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceIndexedTriangle.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceLSS.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceMatrix3x3.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceMatrix3x3.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceMatrix4x4.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceMatrix4x4.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceMemoryMacros.h +# End Source File +# Begin Source File + +SOURCE=.\Ice\IceOBB.cpp # End Source File # Begin Source File -SOURCE=.\OPC_AABB.h +SOURCE=.\Ice\IceOBB.h # End Source File # Begin Source File -SOURCE=.\OPC_Container.cpp +SOURCE=.\Ice\IcePairs.h # End Source File # Begin Source File -SOURCE=.\OPC_Container.h +SOURCE=.\Ice\IcePlane.cpp # End Source File # Begin Source File -SOURCE=.\OPC_FPU.h +SOURCE=.\Ice\IcePlane.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix3x3.cpp +SOURCE=.\Ice\IcePoint.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Matrix3x3.h +SOURCE=.\Ice\IcePoint.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix4x4.cpp +SOURCE=.\Ice\IcePreprocessor.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix4x4.h +SOURCE=.\Ice\IceRandom.cpp # End Source File # Begin Source File -SOURCE=.\OPC_MemoryMacros.h +SOURCE=.\Ice\IceRandom.h # End Source File # Begin Source File -SOURCE=.\OPC_OBB.cpp +SOURCE=.\Ice\IceRay.cpp # End Source File # Begin Source File -SOURCE=.\OPC_OBB.h +SOURCE=.\Ice\IceRay.h # End Source File # Begin Source File -SOURCE=.\OPC_Plane.cpp +SOURCE=.\Ice\IceRevisitedRadix.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Plane.h +SOURCE=.\Ice\IceRevisitedRadix.h # End Source File # Begin Source File -SOURCE=.\OPC_Point.cpp +SOURCE=.\Ice\IceSegment.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Point.h +SOURCE=.\Ice\IceSegment.h # End Source File # Begin Source File -SOURCE=.\OPC_Preprocessor.h +SOURCE=.\Ice\IceTriangle.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Ray.cpp +SOURCE=.\Ice\IceTriangle.h # End Source File # Begin Source File -SOURCE=.\OPC_Ray.h +SOURCE=.\Ice\IceTrilist.h # End Source File # Begin Source File -SOURCE=.\OPC_Triangle.cpp +SOURCE=.\Ice\IceTypes.h # End Source File # Begin Source File -SOURCE=.\OPC_Triangle.h +SOURCE=.\Ice\IceUtils.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Types.h +SOURCE=.\Ice\IceUtils.h # End Source File # End Group # Begin Source File diff --git a/OpcodeDistrib/Opcode.dsw b/Opcode.dsw similarity index 100% rename from OpcodeDistrib/Opcode.dsw rename to Opcode.dsw diff --git a/OpcodeDistrib/Opcode.h b/Opcode.h similarity index 66% rename from OpcodeDistrib/Opcode.h rename to Opcode.h index fa5c1e2..ca29a91 100644 --- a/OpcodeDistrib/Opcode.h +++ b/Opcode.h @@ -39,83 +39,34 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Preprocessor -#ifdef OPCODE_EXPORTS - #define OPCODE_API __declspec(dllexport) -#else - #define OPCODE_API __declspec(dllimport) -#endif - -#ifndef __ICECORE_H__ - #ifdef WIN32 - #include - #include - #endif // WIN32 - - #include - #include - #include - #include - - #ifndef ASSERT - #define ASSERT assert +#ifndef ICE_NO_DLL + #ifdef OPCODE_EXPORTS + #define OPCODE_API __declspec(dllexport) + #else + #define OPCODE_API __declspec(dllimport) #endif - - #define Log - #define SetIceError false - #define EC_OUTOFMEMORY "Out of memory" - #define Alignment - - #include "OPC_Preprocessor.h" -// #undef ICECORE_API -// #define ICECORE_API __declspec(dllimport) - #define ICECORE_API OPCODE_API - - #include "OPC_Types.h" - #include "OPC_FPU.h" - #include "OPC_MemoryMacros.h" - namespace IceCore - { - #include "OPC_Container.h" - } - using namespace IceCore; +#else + #define OPCODE_API #endif -#ifndef __ICEMATHS_H__ - #include - #define ICEMATHS_API OPCODE_API - namespace IceMaths - { - #include "OPC_Point.h" - #include "OPC_Matrix3x3.h" - #include "OPC_Matrix4x4.h" - #include "OPC_Plane.h" - #include "OPC_Ray.h" - } - using namespace IceMaths; -#endif - -#ifndef __MESHMERIZER_H__ - #define MESHMERIZER_API OPCODE_API - namespace Meshmerizer - { - #include "OPC_Triangle.h" - #include "OPC_AABB.h" - #include "OPC_OBB.h" - #include "OPC_BoundingSphere.h" - } - using namespace Meshmerizer; -#endif + #include "OPC_IceHook.h" namespace Opcode { // Bulk-of-the-work #include "OPC_Settings.h" #include "OPC_Common.h" + #include "OPC_MeshInterface.h" + // Builders #include "OPC_TreeBuilders.h" + // Trees #include "OPC_AABBTree.h" #include "OPC_OptimizedTree.h" + // Models + #include "OPC_BaseModel.h" #include "OPC_Model.h" - #include "OPC_BVTCache.h" + #include "OPC_HybridModel.h" + // Colliders #include "OPC_Collider.h" #include "OPC_VolumeCollider.h" #include "OPC_TreeCollider.h" @@ -123,7 +74,16 @@ #include "OPC_SphereCollider.h" #include "OPC_OBBCollider.h" #include "OPC_AABBCollider.h" + #include "OPC_LSSCollider.h" #include "OPC_PlanesCollider.h" + // Usages + #include "OPC_Picking.h" + // Sweep-and-prune + #include "OPC_BoxPruning.h" + #include "OPC_SweepAndPrune.h" + + FUNCTION OPCODE_API bool InitOpcode(); + FUNCTION OPCODE_API bool CloseOpcode(); } #endif // __OPCODE_H__ diff --git a/Opcode.opt b/Opcode.opt new file mode 100644 index 0000000..f279d3b Binary files /dev/null and b/Opcode.opt differ diff --git a/Opcode.plg b/Opcode.plg new file mode 100644 index 0000000..b087a95 --- /dev/null +++ b/Opcode.plg @@ -0,0 +1,153 @@ + + +
+

Build Log

+

+--------------------Configuration: Opcode - Win32 Release-------------------- +

+

Command Lines

+Creating temporary file "F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP245.tmp" with contents +[ +/nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Fp"Release/Opcode.pch" /Yu"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /QIfist /c +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_BaseModel.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_HybridModel.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_Model.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Opcode.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_Collider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_VolumeCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_AABBCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_LSSCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_OBBCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_PlanesCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_RayCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_SphereCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_TreeCollider.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_AABBTree.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_Common.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_MeshInterface.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_OptimizedTree.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_TreeBuilders.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_BoxPruning.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_SweepAndPrune.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\OPC_Picking.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceAABB.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceContainer.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceHPoint.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceIndexedTriangle.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceMatrix3x3.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceMatrix4x4.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceOBB.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IcePlane.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IcePoint.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceRandom.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceRay.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceRevisitedRadix.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceSegment.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceTriangle.cpp" +"Y:\APIs\Ice\OpcodeDistrib\Opcode\Ice\IceUtils.cpp" +] +Creating command line "cl.exe @F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP245.tmp" +Creating temporary file "F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP246.tmp" with contents +[ +/nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Fp"Release/Opcode.pch" /Yc"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /QIfist /c +"Y:\APIs\Ice\OpcodeDistrib\Opcode\StdAfx.cpp" +] +Creating command line "cl.exe @F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP246.tmp" +Creating temporary file "F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP247.tmp" with contents +[ +/nologo /dll /incremental:no /pdb:"Release/Opcode.pdb" /machine:I386 /out:"Release/Opcode.dll" /implib:"Release/Opcode.lib" +.\Release\OPC_BaseModel.obj +.\Release\OPC_HybridModel.obj +.\Release\OPC_Model.obj +.\Release\Opcode.obj +.\Release\StdAfx.obj +.\Release\OPC_Collider.obj +.\Release\OPC_VolumeCollider.obj +.\Release\OPC_AABBCollider.obj +.\Release\OPC_LSSCollider.obj +.\Release\OPC_OBBCollider.obj +.\Release\OPC_PlanesCollider.obj +.\Release\OPC_RayCollider.obj +.\Release\OPC_SphereCollider.obj +.\Release\OPC_TreeCollider.obj +.\Release\OPC_AABBTree.obj +.\Release\OPC_Common.obj +.\Release\OPC_MeshInterface.obj +.\Release\OPC_OptimizedTree.obj +.\Release\OPC_TreeBuilders.obj +.\Release\OPC_BoxPruning.obj +.\Release\OPC_SweepAndPrune.obj +.\Release\OPC_Picking.obj +.\Release\IceAABB.obj +.\Release\IceContainer.obj +.\Release\IceHPoint.obj +.\Release\IceIndexedTriangle.obj +.\Release\IceMatrix3x3.obj +.\Release\IceMatrix4x4.obj +.\Release\IceOBB.obj +.\Release\IcePlane.obj +.\Release\IcePoint.obj +.\Release\IceRandom.obj +.\Release\IceRay.obj +.\Release\IceRevisitedRadix.obj +.\Release\IceSegment.obj +.\Release\IceTriangle.obj +.\Release\IceUtils.obj +] +Creating command line "link.exe @F:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\RSP247.tmp" +

Output Window

+Compiling... +StdAfx.cpp +Compiling OPCODE +Compiling on Windows... +Compiling with VC++... +Compiling... +OPC_BaseModel.cpp +OPC_HybridModel.cpp +OPC_Model.cpp +Opcode.cpp +OPC_Collider.cpp +OPC_VolumeCollider.cpp +OPC_AABBCollider.cpp +OPC_LSSCollider.cpp +OPC_OBBCollider.cpp +OPC_PlanesCollider.cpp +OPC_RayCollider.cpp +OPC_SphereCollider.cpp +OPC_TreeCollider.cpp +OPC_AABBTree.cpp +OPC_Common.cpp +OPC_MeshInterface.cpp +OPC_OptimizedTree.cpp +OPC_TreeBuilders.cpp +OPC_BoxPruning.cpp +OPC_SweepAndPrune.cpp +Generating Code... +Compiling... +OPC_Picking.cpp +IceAABB.cpp +IceContainer.cpp +IceHPoint.cpp +IceIndexedTriangle.cpp +IceMatrix3x3.cpp +IceMatrix4x4.cpp +IceOBB.cpp +IcePlane.cpp +IcePoint.cpp +IceRandom.cpp +IceRay.cpp +IceRevisitedRadix.cpp +IceSegment.cpp +IceTriangle.cpp +IceUtils.cpp +Generating Code... +Linking... + Creating library Release/Opcode.lib and object Release/Opcode.exp + + + +

Results

+Opcode.dll - 0 error(s), 0 warning(s) +
+ + diff --git a/OpcodeDistrib/OPC_AABBTree.h b/OpcodeDistrib/OPC_AABBTree.h deleted file mode 100644 index eb4aaa9..0000000 --- a/OpcodeDistrib/OPC_AABBTree.h +++ /dev/null @@ -1,87 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for a versatile AABB tree. - * \file OPC_AABBTree.h - * \author Pierre Terdiman - * \date March, 20, 2001 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __OPC_AABBTREE_H__ -#define __OPC_AABBTREE_H__ - - //! TO BE DOCUMENTED - #define IMPLEMENT_TREE(baseclass, volume) \ - public: \ - /* Constructor / Destructor */ \ - baseclass(); \ - ~baseclass(); \ - /* Data access */ \ - inline_ const volume* Get##volume() const { return &mBV; } \ - inline_ const baseclass* GetPos() const { return mP; } \ - inline_ const baseclass* GetNeg() const { return mN; } \ - \ - inline_ bool IsLeaf() const { return (!mP && !mN); } \ - \ - /* Stats */ \ - inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ - protected: \ - /* Tree-independent data */ \ - /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ - /* Whatever happens we need the two children and the enclosing volume.*/ \ - volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ - baseclass* mP; \ - baseclass* mN; - - class OPCODE_API AABBTreeNode - { - IMPLEMENT_TREE(AABBTreeNode, AABB) - public: - // Data access - inline_ const udword* GetPrimitives() const { return mNodePrimitives; } - inline_ udword GetNbPrimitives() const { return mNbPrimitives; } - - protected: - // Tree-dependent data - udword* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) - udword mNbPrimitives; //!< Number of primitives for this node - // Internal methods - udword Split(udword axis, AABBTreeBuilder* builder); - bool Subdivide(AABBTreeBuilder* builder); - void _BuildHierarchy(AABBTreeBuilder* builder); - }; - - class OPCODE_API AABBTree : public AABBTreeNode - { - public: - // Constructor / Destructor - AABBTree(); - ~AABBTree(); - // Build - bool Build(AABBTreeBuilder* builder); - // Data access - inline_ const udword* GetIndices() const { return mIndices; } //!< Catch the indices - inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes - - // Infos - bool IsComplete() const; - // Stats - udword ComputeDepth() const; - udword GetUsedBytes() const; - private: - udword* mIndices; //!< Indices in the app list. Indices are reorganized during build. - // Stats - udword mTotalNbNodes; //!< Number of nodes in the tree. - }; - -#endif // __OPC_AABBTREE_H__ diff --git a/OpcodeDistrib/OPC_BVTCache.h b/OpcodeDistrib/OPC_BVTCache.h deleted file mode 100644 index e7b809f..0000000 --- a/OpcodeDistrib/OPC_BVTCache.h +++ /dev/null @@ -1,69 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains various caches. - * \file OPC_BVTCache.h - * \author Pierre Terdiman - * \date May, 12, 2001 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __OPC_BVTCACHE_H__ -#define __OPC_BVTCACHE_H__ - - //! This structure holds cached information used by the algorithm. - //! Two model pointers and two colliding primitives are cached. Model pointers are assigned - //! to their respective meshes, and the pair of colliding primitives is used for temporal - //! coherence. That is, in case temporal coherence is enabled, those two primitives are - //! tested for overlap before everything else. If they still collide, we're done before - //! even entering the recursive collision code. - struct OPCODE_API BVTCache : Pair - { - //! Constructor - inline_ BVTCache() - { - ResetCache(); - ResetCountDown(); - } - - void ResetCache() - { - Model0 = null; - Model1 = null; - id0 = 0; - id1 = 1; -#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! - HullTest = true; - SepVector.pid = 0; - SepVector.qid = 0; - SepVector.SV = Point(1.0f, 0.0f, 0.0f); -#endif // __MESHMERIZER_H__ - } - - inline_ void ResetCountDown() - { -#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! - CountDown = 50; -#endif // __MESHMERIZER_H__ - } - - OPCODE_Model* Model0; //!< Model for first object - OPCODE_Model* Model1; //!< Model for second object - -#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! - SVCache SepVector; - udword CountDown; - bool HullTest; -#endif // __MESHMERIZER_H__ - }; - -#endif // __OPC_BVTCACHE_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_Matrix3x3.cpp b/OpcodeDistrib/OPC_Matrix3x3.cpp deleted file mode 100644 index 35de946..0000000 --- a/OpcodeDistrib/OPC_Matrix3x3.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 3x3 matrices. - * \file IceMatrix3x3.cpp - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * 3x3 matrix. - * DirectX-compliant, ie row-column order, ie m[Row][Col]. - * Same as: - * m11 m12 m13 first row. - * m21 m22 m23 second row. - * m31 m32 m33 third row. - * Stored in memory as m11 m12 m13 m21... - * - * Multiplication rules: - * - * [x'y'z'] = [xyz][M] - * - * x' = x*m11 + y*m21 + z*m31 - * y' = x*m12 + y*m22 + z*m32 - * z' = x*m13 + y*m23 + z*m33 - * - * \class Matrix3x3 - * \author Pierre Terdiman - * \version 1.0 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -using namespace IceMaths; - -// Cast operator -Matrix3x3::operator Matrix4x4() const -{ - return Matrix4x4( - m[0][0], m[0][1], m[0][2], 0.0f, - m[1][0], m[1][1], m[1][2], 0.0f, - m[2][0], m[2][1], m[2][2], 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Creates a rotation matrix that rotates a vector "from" into another vector "to". - * Original code by Tomas Möller. It has been modified to match ICE maths conventions (vector * matrix) - * \param from [in] normalized source vector - * \param to [in] normalized destination vector - * \return Self-Reference - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix3x3& Matrix3x3::FromTo(const Point& from, const Point& to) -{ - #define EPSILON 0.000001f - - Point v = from^to; - float e = from|to; - - // "from" almost or equal to "to"-vector? - if(e>1.0f - EPSILON) - { - // return identity - Identity(); - } - // "from" almost or equal to negated "to"? - else if(e<-1.0f + EPSILON) - { - // Left= from ^ (1,0,0) - Point Left(0.0f, from.z, -from.y); - // Was left=CROSS(from,(1,0,0)) a good choice? - if((Left|Left)eZ() = this->eZ().Unit(); -// this->eY() = (this->eZ() * this->eX()).Unit(); -// this->eX() = this->eY() * this->eZ(); - return *this; - } - - //! this = exp(a) - Matrix3x3& Exp(const Matrix3x3& a) - { - const int NbTerms = 100; - Matrix3x3 Term; // next term in series - Matrix3x3 Temp; - - Identity(); - Term.Identity(); - - for(udword div=1; div<=NbTerms; div++) - { - Temp.Mult(Term, a); - Term.Mult(Temp, 1.0f / float(div)); - // find next Term = Term * a / div - Add(Term); - } - return *this; - } - -void FromQuat(const Quat &q); -void FromQuatL2(const Quat &q, float l2); - - // Arithmetic operators - //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3; - inline_ Matrix3x3 operator+(const Matrix3x3& mat) const - { - return Matrix3x3( - m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2], - m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2], - m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]); - } - - //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3; - inline_ Matrix3x3 operator-(const Matrix3x3& mat) const - { - return Matrix3x3( - m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2], - m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2], - m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]); - } - - //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3; - inline_ Matrix3x3 operator*(const Matrix3x3& mat) const - { - return Matrix3x3( - m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0], - m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1], - m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2], - - m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0], - m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1], - m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2], - - m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0], - m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1], - m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]); - } - - //! Operator for Point Mul = Matrix3x3 * Point; - inline_ Point operator*(const Point& v) const { return Point(ROW[0]|v, ROW[1]|v, ROW[2]|v); } - - //! Operator for Matrix3x3 Mul = Matrix3x3 * float; - inline_ Matrix3x3 operator*(float s) const - { - return Matrix3x3( - m[0][0]*s, m[0][1]*s, m[0][2]*s, - m[1][0]*s, m[1][1]*s, m[1][2]*s, - m[2][0]*s, m[2][1]*s, m[2][2]*s); - } - - //! Operator for Matrix3x3 Mul = float * Matrix3x3; - inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat) - { - return Matrix3x3( - s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], - s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], - s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]); - } - - //! Operator for Matrix3x3 Div = Matrix3x3 / float; - inline_ Matrix3x3 operator/(float s) const - { - if (s) s = 1.0f / s; - return Matrix3x3( - m[0][0]*s, m[0][1]*s, m[0][2]*s, - m[1][0]*s, m[1][1]*s, m[1][2]*s, - m[2][0]*s, m[2][1]*s, m[2][2]*s); - } - - //! Operator for Matrix3x3 Div = float / Matrix3x3; - inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat) - { - return Matrix3x3( - s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], - s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], - s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]); - } - - //! Operator for Matrix3x3 += Matrix3x3 - inline_ Matrix3x3& operator+=(const Matrix3x3& mat) - { - m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; - m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; - m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; - return *this; - } - - //! Operator for Matrix3x3 -= Matrix3x3 - inline_ Matrix3x3& operator-=(const Matrix3x3& mat) - { - m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; - m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; - m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; - return *this; - } - - //! Operator for Matrix3x3 *= Matrix3x3 - inline_ Matrix3x3& operator*=(const Matrix3x3& mat) - { - Point TempRow; - - GetRow(0, TempRow); - m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; - m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; - m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; - - GetRow(1, TempRow); - m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; - m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; - m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; - - GetRow(2, TempRow); - m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; - m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; - m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; - return *this; - } - - //! Operator for Matrix3x3 *= float - inline_ Matrix3x3& operator*=(float s) - { - m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; - m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; - m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; - return *this; - } - - //! Operator for Matrix3x3 /= float - inline_ Matrix3x3& operator/=(float s) - { - if (s) s = 1.0f / s; - m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; - m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; - m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; - return *this; - } - - // Cast operators - //! Cast a Matrix3x3 to a Matrix4x4. - operator Matrix4x4() const; - //! Cast a Matrix3x3 to a Quat. - operator Quat() const; - - inline_ const Point* operator[](int nRow) const { return (const Point*)&m[nRow][0]; } - inline_ Point* operator[](int nRow) { return (Point*)&m[nRow][0]; } - - public: - - float m[3][3]; - }; - -#endif // __ICEMATRIX3X3_H__ - diff --git a/OpcodeDistrib/OPC_Matrix4x4.cpp b/OpcodeDistrib/OPC_Matrix4x4.cpp deleted file mode 100644 index 037bba0..0000000 --- a/OpcodeDistrib/OPC_Matrix4x4.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 4x4 matrices. - * \file IceMatrix4x4.cpp - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * 4x4 matrix. - * DirectX-compliant, ie row-column order, ie m[Row][Col]. - * Same as: - * m11 m12 m13 m14 first row. - * m21 m22 m23 m24 second row. - * m31 m32 m33 m34 third row. - * m41 m42 m43 m44 fourth row. - * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1). - * Stored in memory as m11 m12 m13 m14 m21... - * - * Multiplication rules: - * - * [x'y'z'1] = [xyz1][M] - * - * x' = x*m11 + y*m21 + z*m31 + m41 - * y' = x*m12 + y*m22 + z*m32 + m42 - * z' = x*m13 + y*m23 + z*m33 + m43 - * 1' = 0 + 0 + 0 + m44 - * - * \class Matrix4x4 - * \author Pierre Terdiman - * \version 1.0 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -using namespace IceMaths; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * A global function to invert a PR matrix. (which only contains a rotation and a translation) - * \relates Matrix4x4 - * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) - * \param dest [out] destination matrix - * \param src [in] source matrix - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) -{ - dest.m[0][0] = src.m[0][0]; - dest.m[1][0] = src.m[0][1]; - dest.m[2][0] = src.m[0][2]; - dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]); - - dest.m[0][1] = src.m[1][0]; - dest.m[1][1] = src.m[1][1]; - dest.m[2][1] = src.m[1][2]; - dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]); - - dest.m[0][2] = src.m[2][0]; - dest.m[1][2] = src.m[2][1]; - dest.m[2][2] = src.m[2][2]; - dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]); - - dest.m[0][3] = 0.0f; - dest.m[1][3] = 0.0f; - dest.m[2][3] = 0.0f; - dest.m[3][3] = 1.0f; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Compute the cofactor of the Matrix at a specified location -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float Matrix4x4::CoFactor(udword row, udword col) const -{ - return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] + - m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] + - m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3]) - - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] + - m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] + - m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Compute the determinant of the Matrix -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -float Matrix4x4::Determinant() const -{ - return m[0][0] * CoFactor(0, 0) + - m[0][1] * CoFactor(0, 1) + - m[0][2] * CoFactor(0, 2) + - m[0][3] * CoFactor(0, 3); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Compute the inverse of the matrix -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix4x4& Matrix4x4::Invert() -{ - float Det = Determinant(); - Matrix4x4 Temp; - - if(fabsf(Det) < MATRIX4X4_EPSILON) - return *this; // The matrix is not invertible! Singular case! - - float IDet = 1.0f / Det; - - Temp.m[0][0] = CoFactor(0,0) * IDet; - Temp.m[1][0] = CoFactor(0,1) * IDet; - Temp.m[2][0] = CoFactor(0,2) * IDet; - Temp.m[3][0] = CoFactor(0,3) * IDet; - Temp.m[0][1] = CoFactor(1,0) * IDet; - Temp.m[1][1] = CoFactor(1,1) * IDet; - Temp.m[2][1] = CoFactor(1,2) * IDet; - Temp.m[3][1] = CoFactor(1,3) * IDet; - Temp.m[0][2] = CoFactor(2,0) * IDet; - Temp.m[1][2] = CoFactor(2,1) * IDet; - Temp.m[2][2] = CoFactor(2,2) * IDet; - Temp.m[3][2] = CoFactor(2,3) * IDet; - Temp.m[0][3] = CoFactor(3,0) * IDet; - Temp.m[1][3] = CoFactor(3,1) * IDet; - Temp.m[2][3] = CoFactor(3,2) * IDet; - Temp.m[3][3] = CoFactor(3,3) * IDet; - - *this = Temp; - - return *this; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Compute a shadow matrix, used to draw planar stencil shadows. - * \param light [in] light source position. - * \param p0 [in] first point on the projection plane. - * \param p1 [in] second point on the projection plane. - * \param p2 [in] third point on the projection plane. - * \return Self-Reference - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix4x4& Matrix4x4::Shadow(const Point& light, const Point& p0, const Point& p1, const Point& p2) -{ - // The 3 input vertices form the projection plane. - - // Compute the plane equation - Point n = ((p0-p1)^(p1-p2)).Normalize(); - float D = -(p0|n); - Plane PlaneEquation; - float Coeff; - if(fabsf(D)<0.0001f) Coeff = -1.0f; - else Coeff = -1.0f / fabsf(D); - PlaneEquation.n.x = n.x * Coeff; - PlaneEquation.n.y = n.y * Coeff; - PlaneEquation.n.z = n.z * Coeff; - PlaneEquation.d = D * Coeff; - - // Plane equation must be normalized! - float dot = PlaneEquation.n.x*light.x + PlaneEquation.n.y*light.y + PlaneEquation.n.z*light.z + PlaneEquation.d; - - m[0][0] = dot - light.x*PlaneEquation.n.x; - m[1][0] = - light.x*PlaneEquation.n.y; - m[2][0] = - light.x*PlaneEquation.n.z; - m[3][0] = - light.x*PlaneEquation.d; - - m[0][1] = - light.y*PlaneEquation.n.x; - m[1][1] = dot - light.y*PlaneEquation.n.y; - m[2][1] = - light.y*PlaneEquation.n.z; - m[3][1] = - light.y*PlaneEquation.d; - - m[0][2] = - light.z*PlaneEquation.n.x; - m[1][2] = - light.z*PlaneEquation.n.y; - m[2][2] = dot - light.z*PlaneEquation.n.z; - m[3][2] = - light.z*PlaneEquation.d; - - m[0][3] = - PlaneEquation.n.x; - m[1][3] = - PlaneEquation.n.y; - m[2][3] = - PlaneEquation.n.z; - m[3][3] = dot - PlaneEquation.d; - - return *this; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Compute a sphere map matrix. - * \param scale [in] scale factor. - * \return Self-Reference - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix4x4& Matrix4x4::SphereMap(float scale) -{ - Identity(); - m[0][0] = scale; - m[1][1] = scale; - m[3][0] = 0.5f; - m[3][1] = 0.5f; - return *this; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Compute a self-shadowing matrix. - * \param light [in] light source position. - * \return Self-Reference - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix4x4& Matrix4x4::SelfShadow(const Point& light) -{ - Point Light = light; - Light.Normalize(); - - Zero(); - m[0][0] = Light.x * 0.5f; - m[0][1] = Light.y * 0.5f; - m[0][2] = Light.z * 0.5f; - m[0][3] = 0.5f; - m[3][3] = 1.0f; - return *this; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Compute a rotozoom matrix. - * \param angle [in] rotation angle - * \param zoom [in] zoom factor - * \param posx [in] x translation - * \param posy [in] y translation - * \return Self-Reference - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Matrix4x4& Matrix4x4::Rotozoom(float angle, float zoom, float posx, float posy) -{ - RotZ(angle); - Scale(zoom, zoom, zoom); - SetTrans(posx, posy, 0.0f); - return *this; -} - -// ### must be optimized... consider using the 3x3 version -Matrix4x4& Matrix4x4::Rot(float angle, Point& p1, Point& p2) -{ - Point Axis = (p2 - p1).Normalize(); - - Matrix4x4 T, InvT; - T.Identity(); - T.SetTrans(-p1.x, -p1.y, -p1.z); - InvT = T; - InvT.Invert(); - - Matrix4x4 Rx, InvRx; - Rx.Identity(); - float d = sqrtf(Axis.y*Axis.y + Axis.z*Axis.z); - if(d!=0.0f) - { - float CosAngle = Axis.z / d; - float SinAngle = Axis.y / d; - Rx.SetRow(1, Point(0.0f, CosAngle, SinAngle)); - Rx.SetRow(2, Point(0.0f, -SinAngle, CosAngle)); - } - InvRx = Rx; - InvRx.Invert(); - - Matrix4x4 Ry, InvRy; - Ry.Identity(); - Ry.SetRow(0, Point(d, 0.0f, Axis.x)); - Ry.SetRow(2, Point(-Axis.x, 0.0f, d)); - InvRy = Ry; - InvRy.Invert(); - - Matrix4x4 Rz; - Rz.RotZ(angle); - - Matrix4x4 Combo = T * Rx * Ry * Rz * InvRy * InvRx * InvT; - *this = Combo; - return *this; -} - - -#ifdef OLDIES - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// LU Backward substitution -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Input : -// - indx, -// - b, -// Output : None -// Return : None -// Exception: None -// Remark : None - -void Matrix::LUBackwardSubstitution( sdword *indx, float *b ) -{ - sdword i, j, ii=-1, ip; - float sum; - - for ( i=0; i<4; i++ ) - { - ip = indx[i]; - sum = b[ip]; - b[ip] = b[i]; - - if (ii>=0) - { - for (j=ii; j<=i-1; j++) - sum -= (*this)(i, j) * b[j]; - } - else if (sum != 0.0f) - ii = i; - b[i] = sum; - } - - for ( i=3; i>=0; i-- ) - { - sum = b[i]; - for (j=i+1; j<4; j++) - sum -= (*this)(i, j) * b[j]; - b[i] = sum / (*this)(i, i); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// LU decomposition -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Input : -// - indx, -// - d, -// Output : None -// Return : None -// Exception: None -// Remark : None - -void Matrix::LUDecomposition( sdword* indx, float* d ) -{ - float vv[4]; /* implicit scale for each row */ - float big, dum, sum, tmp; - sdword i, imax, j, k; - - *d = 1.0f; - for ( i=0; i<4; i++ ) - { - big = 0.0f; - for (j=0; j<4; j++) - if ((tmp = (float) fabsf( (*this)(i, j) )) > big) - big = tmp; - /* - if (big == 0.0f) { - printf("ludcmp(): singular matrix found...\n"); - exit(1); - } - */ - vv[i] = 1.0f/big; - } - for ( j=0; j<4; j++ ) - { - for ( i=0; i= big) - { - big = dum; - imax = i; - } - } - if (j != imax) - { - for (k=0; k<4; k++) - { - dum = (*this)(imax, k); - (*this)(imax, k) = (*this)(j, k); - (*this)(j, k) = dum; - } - *d = -(*d); - vv[imax] = vv[j]; - } - indx[j] = imax; - if ((*this)(j, j) == 0.0f) - (*this)(j, j) = 1.0e-20f; /* can be 0.0 also... */ - if (j != 3) - { - dum = 1.0f / (*this)(j, j); - for (i=j+1; i<4; i++) - (*this)(i, j) *= dum; - } - } -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// A method to compute a view matrix from an angle axis -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Input : axis, angle the view parameters -// Output : None -// Return : None -// Exception: None -// Remark : None - -Matrix& Matrix::ComputeAxisMatrix(Point& axis, float angle) -{ - MakeIdentity(); - - float length = axis.Magnitude(); - // Normalize the z basis vector - axis /= length; - - // Get the dot product, and calculate the projection of the z basis - // vector onto the up vector. The projection is the y basis vector. - float dotProduct = Point(0, 1, 0) | axis; - Point Up = Point(0, 1, 0) - dotProduct * axis; - - // This is to prevent bogus view matrix (up view vector equals to axis) - if (Up.Magnitude() < 1e-6f) { - Up = Point(0, 0, 1); - } - else { - // Normalize the y basis vector - Up /= length; - Up.Normalize(); - } - - // The x basis vector is found simply with the cross product of the y - // and z basis vectors - Point Right = Up ^ axis; - - SetCol( 0, Right ); - SetCol( 1, Up ); - SetCol( 2, axis ); - Transpose(); - - return *this; -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Operator to cast a matrix to a PRS -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Input : None -// Output : None -// Return : None -// Exception: None -// Remark : None - -Matrix::operator PRS() const -{ - PRS Cast; - - udword dwRow; - Matrix3x3 Orthonormal; - float ScaleFactor; - Point Scale, Row, NormalizedRow; - - if ( IsIdentity() ) - { // The special case of the identity matrix - Cast.SetScale( 1.0f, 1.0f, 1.0f ).SetQuaternion( (Quat) (*this) ); - } - else - { - for ( dwRow=0; dwRow<3; dwRow++ ) - { - Row = *GetRow( dwRow ); - Scale[dwRow] = ScaleFactor = Row.Magnitude(); - - if ( fabsf(ScaleFactor) > mEpsilon ) - NormalizedRow = Row / ScaleFactor; - else - { - NormalizedRow[0] = NormalizedRow[1] = NormalizedRow[2] = 0.0f; - NormalizedRow[dwRow] = 1.0f; - } - Orthonormal.SetRow( dwRow, NormalizedRow ); - } - - // Build the final PRS - Cast.SetQuaternion( (Quat) Orthonormal ).SetPosition( *GetRow( 3 ) ).SetScale( Scale ); - } - - return Cast; -} - -#endif \ No newline at end of file diff --git a/OpcodeDistrib/OPC_Matrix4x4.h b/OpcodeDistrib/OPC_Matrix4x4.h deleted file mode 100644 index 00d8cf5..0000000 --- a/OpcodeDistrib/OPC_Matrix4x4.h +++ /dev/null @@ -1,453 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 4x4 matrices. - * \file IceMatrix4x4.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEMATRIX4X4_H__ -#define __ICEMATRIX4X4_H__ - - // Forward declarations - class PRS; - class PR; - - #define MATRIX4X4_EPSILON (1.0e-7f) - #define ROW *(*this) - - class ICEMATHS_API Matrix4x4 - { -// void LUBackwardSubstitution( sdword *indx, float* b ); -// void LUDecomposition( sdword* indx, float* d ); - - public: - //! Empty constructor. - inline_ Matrix4x4() {} - //! Constructor from 16 values - inline_ Matrix4x4( float m00, float m01, float m02, float m03, - float m10, float m11, float m12, float m13, - float m20, float m21, float m22, float m23, - float m30, float m31, float m32, float m33) - { - m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; - m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; - m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; - m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; - } - //! Copy constructor - inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } - //! Destructor. - inline_ ~Matrix4x4() {} - - //! Assign values (rotation only) - inline_ Matrix4x4& Set( float m00, float m01, float m02, - float m10, float m11, float m12, - float m20, float m21, float m22) - { - m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; - m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; - m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; - return *this; - } - //! Assign values - inline_ Matrix4x4& Set( float m00, float m01, float m02, float m03, - float m10, float m11, float m12, float m13, - float m20, float m21, float m22, float m23, - float m30, float m31, float m32, float m33) - { - m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; - m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; - m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; - m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; - return *this; - } - // Translation - //! Returns the translation part of the matrix. - inline_ const HPoint* GetTrans() const { return (*this)[3];/*(Point*) &mTrans;*/ } - //! Gets the translation part of the matrix - inline_ void GetTrans(Point& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; } - //! Sets the translation part of the matrix, from a Point. - inline_ void SetTrans(const Point& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; } - //! Sets the translation part of the matrix, from a HPoint. -// inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; } - //! Sets the translation part of the matrix, from floats. - inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; } - - // Scale - //! Sets the scale from a Point. The point is put on the diagonal. - inline_ void SetScale(const Point& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; } - //! Sets the scale from floats. Values are put on the diagonal. - inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; } - //! Scales from a Point. Each row is multiplied by a component. - void Scale(const Point& p) - { - m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z; - m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z; - m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z; - } - //! Scales from floats. Each row is multiplied by a value. - void Scale(float sx, float sy, float sz) - { - m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz; - m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz; - m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz; - } - - //! Copy from a Matrix4x4 - inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); } - - // Row-column access - //! Returns a row. -// inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; } - //! Returns a row. - inline_ void GetRow(const udword r, Point& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; } - //! Sets a row. -// inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; } - //! Sets a row. - inline_ void SetRow(const udword r, const Point& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; } - //! Returns a column. -// inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; } - //! Returns a column. - inline_ void GetCol(const udword c, Point& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; } - //! Sets a column. -// inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; } - //! Sets a column. - inline_ void SetCol(const udword c, const Point& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; } - -/* - //! Returns a row. - inline_ HPoint GetRow(const udword row) const { return mRow[row]; } - //! Sets a row. - inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; } - //! Sets a row. - Matrix4x4& SetRow(const udword row, const Point& p) - { - m[row][0] = p.x; - m[row][1] = p.y; - m[row][2] = p.z; - m[row][3] = (row != 3) ? 0.0f : 1.0f; - return *this; - } - //! Returns a column. - HPoint GetCol(const udword col) const - { - HPoint Res; - Res.x = m[0][col]; - Res.y = m[1][col]; - Res.z = m[2][col]; - Res.w = m[3][col]; - return Res; - } - //! Sets a column. - Matrix4x4& SetCol(const udword col, const HPoint& p) - { - m[0][col] = p.x; - m[1][col] = p.y; - m[2][col] = p.z; - m[3][col] = p.w; - return *this; - } - //! Sets a column. - Matrix4x4& SetCol(const udword col, const Point& p) - { - m[0][col] = p.x; - m[1][col] = p.y; - m[2][col] = p.z; - m[3][col] = (col != 3) ? 0.0f : 1.0f; - return *this; - } -*/ - //! Computes the trace. The trace is the sum of the 4 diagonal components. - inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; } - //! Computes the trace of the upper 3x3 matrix. - inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; } - //! Clears the matrix. - inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } - //! Sets the identity matrix. - inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; } - //! Checks for identity - inline_ bool IsIdentity() const - { - if(IR(m[0][0])!=IEEE_1_0) return false; - if(IR(m[0][1])!=0) return false; - if(IR(m[0][2])!=0) return false; - if(IR(m[0][3])!=0) return false; - - if(IR(m[1][0])!=0) return false; - if(IR(m[1][1])!=IEEE_1_0) return false; - if(IR(m[1][2])!=0) return false; - if(IR(m[1][3])!=0) return false; - - if(IR(m[2][0])!=0) return false; - if(IR(m[2][1])!=0) return false; - if(IR(m[2][2])!=IEEE_1_0) return false; - if(IR(m[2][3])!=0) return false; - - if(IR(m[3][0])!=0) return false; - if(IR(m[3][1])!=0) return false; - if(IR(m[3][2])!=0) return false; - if(IR(m[3][3])!=IEEE_1_0) return false; - return true; - } - - // Computes a world matrix. - Matrix4x4& World(const PRS& prs); - Matrix4x4& World(const PR& pr); - // Computes a shadow matrix - Matrix4x4& Shadow(const Point& light, const Point& p0, const Point& p1, const Point& p2); - // Computes a sphere map matrix - Matrix4x4& SphereMap(float scale=0.5f); - // Computes a self-shadowing matrix - Matrix4x4& SelfShadow(const Point& light); - // Computes a rotozoom matrix - Matrix4x4& Rotozoom(float angle, float zoom, float posx, float posy); - - //! Sets a rotation matrix around the X axis. - void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; } - //! Sets a rotation matrix around the Y axis. - void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; } - //! Sets a rotation matrix around the Z axis. - void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; } - - //! Makes a rotation matrix about an arbitrary angle - Matrix4x4& Rot(float angle, Point& p1, Point& p2); - - //! Transposes the matrix. - void Transpose() - { - IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]); - IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]); - IR(m[3][0]) ^= IR(m[0][3]); IR(m[0][3]) ^= IR(m[3][0]); IR(m[3][0]) ^= IR(m[0][3]); - IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); - IR(m[1][3]) ^= IR(m[3][1]); IR(m[3][1]) ^= IR(m[1][3]); IR(m[1][3]) ^= IR(m[3][1]); - IR(m[2][3]) ^= IR(m[3][2]); IR(m[3][2]) ^= IR(m[2][3]); IR(m[2][3]) ^= IR(m[3][2]); - } - - //! Computes a cofactor. Used for matrix inversion. - float CoFactor(udword row, udword col) const; - //! Computes the determinant of the matrix. - float Determinant() const; - //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted. - Matrix4x4& Invert(); -// Matrix& ComputeAxisMatrix(Point& axis, float angle); - - // Cast operators - //! Casts a Matrix4x4 to a Matrix3x3. - inline_ operator Matrix3x3() const - { - return Matrix3x3( - m[0][0], m[0][1], m[0][2], - m[1][0], m[1][1], m[1][2], - m[2][0], m[2][1], m[2][2]); - } - //! Casts a Matrix4x4 to a Quat. - operator Quat() const; - //! Casts a Matrix4x4 to a PR. - operator PR() const; - - // Arithmetic operators - //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4; -/* inline_ Matrix4x4 operator+(const Matrix4x4& mat) const - { - Matrix4x4 Ret; - Ret.SetRow(0, ROW[0] + *mat[0]); - Ret.SetRow(1, ROW[1] + *mat[1]); - Ret.SetRow(2, ROW[2] + *mat[2]); - Ret.SetRow(3, ROW[3] + *mat[3]); - return Ret; - } - - //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4; - inline_ Matrix4x4 operator-(const Matrix4x4& mat) const - { - Matrix4x4 Ret; - Ret.SetRow(0, ROW[0] - *mat[0]); - Ret.SetRow(1, ROW[1] - *mat[1]); - Ret.SetRow(2, ROW[2] - *mat[2]); - Ret.SetRow(3, ROW[3] - *mat[3]); - return Ret; - } -*/ - //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4; - inline_ Matrix4x4 operator*(const Matrix4x4& mat) const - { - return Matrix4x4( - m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0], - m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1], - m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2], - m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3], - - m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0], - m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1], - m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2], - m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3], - - m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0], - m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1], - m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2], - m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3], - - m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0], - m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1], - m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2], - m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]); - } - - //! Operator for HPoint Mul = Matrix4x4 * HPoint; -// inline_ HPoint operator*(const HPoint& v) const { return HPoint(ROW[0]|v, ROW[1]|v, ROW[2]|v, ROW[3]|v); } - - //! Operator for Point Mul = Matrix4x4 * Point; - inline_ Point operator*(const Point& v) const - { - return Point( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3], - m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3], - m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] ); - } - - //! Operator for Matrix4x4 Scale = Matrix4x4 * float; - inline_ Matrix4x4 operator*(float s) const - { - return Matrix4x4( - m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, - m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, - m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, - m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); - } - - //! Operator for Matrix4x4 Scale = float * Matrix4x4; - inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat) - { - return Matrix4x4( - s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3], - s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3], - s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3], - s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]); - } - - //! Operator for Matrix4x4 Div = Matrix4x4 / float; - inline_ Matrix4x4 operator/(float s) const - { - if(s) s = 1.0f / s; - - return Matrix4x4( - m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, - m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, - m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, - m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); - } - - //! Operator for Matrix4x4 Div = float / Matrix4x4; - inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat) - { - return Matrix4x4( - s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3], - s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3], - s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3], - s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]); - } - - //! Operator for Matrix4x4 += Matrix4x4; - inline_ Matrix4x4& operator+=(const Matrix4x4& mat) - { - m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3]; - m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3]; - m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3]; - m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3]; - return *this; - } - - //! Operator for Matrix4x4 -= Matrix4x4; - inline_ Matrix4x4& operator-=(const Matrix4x4& mat) - { - m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3]; - m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3]; - m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3]; - m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3]; - return *this; - } - - //! Operator for Matrix4x4 *= Matrix4x4; -/* Matrix4x4& operator*=(const Matrix4x4& mat) - { - HPoint TempRow; - - GetRow(0, TempRow); - m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; - m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; - m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; - m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; - - GetRow(1, TempRow); - m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; - m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; - m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; - m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; - - GetRow(2, TempRow); - m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; - m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; - m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; - m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; - - GetRow(3, TempRow); - m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; - m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; - m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; - m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; - - return *this; - } -*/ - //! Operator for Matrix4x4 *= float; - inline_ Matrix4x4& operator*=(float s) - { - m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; - m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; - m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; - m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; - return *this; - } - - //! Operator for Matrix4x4 /= float; - inline_ Matrix4x4& operator/=(float s) - { - if(s) s = 1.0f / s; - m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; - m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; - m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; - m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; - return *this; - } - - inline_ const HPoint* operator[](int nRow) const { return (const HPoint*)&m[nRow][0]; } - inline_ HPoint* operator[](int nRow) { return (HPoint*)&m[nRow][0]; } - - public: - - float m[4][4]; - }; - - //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix - inline_ void TransformPoint4x3(Point& dest, const Point& source, const Matrix4x4& rot) - { - dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; - dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; - dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; - } - - //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix - inline_ void TransformPoint3x3(Point& dest, const Point& source, const Matrix4x4& rot) - { - dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; - dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; - dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; - } - - ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); - -#endif // __ICEMATRIX4X4_H__ - diff --git a/OpcodeDistrib/OPC_OBB.cpp b/OpcodeDistrib/OPC_OBB.cpp deleted file mode 100644 index 0c94c40..0000000 --- a/OpcodeDistrib/OPC_OBB.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains OBB-related code. - * \file IceOBB.cpp - * \author Pierre Terdiman - * \date January, 29, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * An Oriented Bounding Box (OBB). - * \class OBB - * \author Pierre Terdiman - * \version 1.0 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -using namespace Meshmerizer; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Tests if a point is contained within the OBB. - * \param p [in] the world point to test - * \return true if inside the OBB - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBB::ContainsPoint(const Point& p) const -{ - // Point in OBB test using lazy evaluation and early exits - - // Translate to box space - Point RelPoint = p - mCenter; - - // Point * mRot maps from box space to world space - // mRot * Point maps from world space to box space (what we need here) - - float f = mRot2.m[0][0] * RelPoint.x + mRot2.m[0][1] * RelPoint.y + mRot2.m[0][2] * RelPoint.z; - if(f >= mExtents.x || f <= -mExtents.x) return false; - - f = mRot2.m[1][0] * RelPoint.x + mRot2.m[1][1] * RelPoint.y + mRot2.m[1][2] * RelPoint.z; - if(f >= mExtents.y || f <= -mExtents.y) return false; - - f = mRot2.m[2][0] * RelPoint.x + mRot2.m[2][1] * RelPoint.y + mRot2.m[2][2] * RelPoint.z; - if(f >= mExtents.z || f <= -mExtents.z) return false; - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Builds an OBB from an AABB and a world transform. - * \param aabb [in] the aabb - * \param mat [in] the world transform - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void OBB::Create(const AABB& aabb, const Matrix4x4& mat) -{ - // Note: must be coherent with Rotate() - - aabb.GetCenter(mCenter); - aabb.GetExtents(mExtents); - // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity). - - // So following what's done in Rotate: - // - x-form the center - mCenter *= mat; - // - combine rotation with identity, i.e. just use given matrix - mRot2 = mat; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Computes the obb planes. - * \param planes [out] 6 box planes - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBB::ComputePlanes(Plane* planes) const -{ - // Checkings - if(!planes) return false; - - Point Axis0 = *mRot2[0]; - Point Axis1 = *mRot2[1]; - Point Axis2 = *mRot2[2]; - - // Writes normals - planes[0].n = Axis0; - planes[1].n = -Axis0; - planes[2].n = Axis1; - planes[3].n = -Axis1; - planes[4].n = Axis2; - planes[5].n = -Axis2; - - // Compute a point on each plane - Point p0 = mCenter + Axis0 * mExtents.x; - Point p1 = mCenter - Axis0 * mExtents.x; - Point p2 = mCenter + Axis1 * mExtents.y; - Point p3 = mCenter - Axis1 * mExtents.y; - Point p4 = mCenter + Axis2 * mExtents.z; - Point p5 = mCenter - Axis2 * mExtents.z; - - // Compute d - planes[0].d = -(planes[0].n|p0); - planes[1].d = -(planes[1].n|p1); - planes[2].d = -(planes[2].n|p2); - planes[3].d = -(planes[3].n|p3); - planes[4].d = -(planes[4].n|p4); - planes[5].d = -(planes[5].n|p5); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Computes the obb points. - * \param pts [out] 8 box points - * \return true if success - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool OBB::ComputePoints(Point* pts) const -{ - // Checkings - if(!pts) return false; - - Point Axis0 = *mRot2[0]; - Point Axis1 = *mRot2[1]; - Point Axis2 = *mRot2[2]; - - pts[0] = mCenter - Axis0*mExtents.x + Axis1*mExtents.y + Axis2*mExtents.z; - pts[1] = mCenter + Axis0*mExtents.x + Axis1*mExtents.y + Axis2*mExtents.z; - pts[2] = mCenter + Axis0*mExtents.x - Axis1*mExtents.y + Axis2*mExtents.z; - pts[3] = mCenter - Axis0*mExtents.x - Axis1*mExtents.y + Axis2*mExtents.z; - pts[4] = mCenter - Axis0*mExtents.x + Axis1*mExtents.y - Axis2*mExtents.z; - pts[5] = mCenter + Axis0*mExtents.x + Axis1*mExtents.y - Axis2*mExtents.z; - pts[6] = mCenter + Axis0*mExtents.x - Axis1*mExtents.y - Axis2*mExtents.z; - pts[7] = mCenter - Axis0*mExtents.x - Axis1*mExtents.y - Axis2*mExtents.z; - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Returns a list of indices used to draw the OBB. - * \return 48 indices for the list returned by ComputePoints() - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const udword* OBB::GetLineIndices() const -{ - static udword Indices[] = { - 0, 1, 1, 2, 2, 3, 3, 0, - 1, 5, 5, 6, 6, 2, 2, 1, - 7, 6, 6, 5, 5, 4, 4, 7, - 3, 7, 7, 4, 4, 0, 0, 3, - 4, 5, 5, 1, 1, 0, 0, 4, - 3, 2, 2, 6, 6, 7, 7, 3, - }; - return Indices; -} - diff --git a/OpcodeDistrib/OPC_OBB.h b/OpcodeDistrib/OPC_OBB.h deleted file mode 100644 index 79b3c31..0000000 --- a/OpcodeDistrib/OPC_OBB.h +++ /dev/null @@ -1,132 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains OBB-related code. (oriented bounding box) - * \file IceOBB.h - * \author Pierre Terdiman - * \date January, 13, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEOBB_H__ -#define __ICEOBB_H__ - - class IndexedTriangle; - class LSS; - - class MESHMERIZER_API OBB - { - public: - //! Constructor - inline_ OBB() {} - //! Constructor - inline_ OBB(const Point& center, const Point& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot2(rot) {} - //! Destructor - inline_ ~OBB() {} - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Tests if a point is contained within the OBB. - * \param p [in] the world point to test - * \return true if inside the OBB - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ContainsPoint(const Point& p) const; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Builds an OBB from an AABB and a world transform. - * \param aabb [in] the aabb - * \param mat [in] the world transform - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Create(const AABB& aabb, const Matrix4x4& mat); - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Recomputes the OBB after an arbitrary transform by a 4x4 matrix. - * \param mtx [in] the transform matrix - * \param obb [out] the transformed OBB - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const - { - // The extents remain constant - obb.mExtents = mExtents; - // The center gets x-formed - obb.mCenter = mCenter * mtx; - // Combine rotations - obb.mRot2 = mRot2 * Matrix3x3(mtx); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes the obb planes. - * \param planes [out] 6 box planes - * \return true if success - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ComputePlanes(Plane* planes) const; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes the obb points. - * \param pts [out] 8 box points - * \return true if success - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool ComputePoints(Point* pts) const; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Returns a list of indices used to draw the OBB. - * \return 48 indices for the list returned by ComputePoints() - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const udword* GetLineIndices() const; - - void ComputeLSS(LSS& lss); - - Point mCenter; //!< B for Box - Point mExtents; //!< B for Bounding -// Matrix3x3 mRot; //!< O for Oriented - Matrix3x3 mRot2; //!< O for Oriented - - // Orientation is stored in row-major format, - // i.e. rows = eigen vectors of the covariance matrix - }; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Eigen analysis of a triangular mesh. - * \param tris [in] list of triangles - * \param nbtris [in] number of triangles - * \param verts [in] list of vertices - * \param nbverts [in] number of vertices - * \param center [out] center of mesh - * \param eigenvalues [out] eigen values - * \param eigenvectors [out] eigen vectors (stored in columns) - * \param usehull [in] true to use the convex hull - * \return true if success - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FUNCTION MESHMERIZER_API bool ComputeSpread(const IndexedTriangle* tris, udword nbtris, const Point* verts, udword nbverts, Point& center, Point& eigenvalues, Matrix3x3& eigenvectors, bool usehull=false); - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes an OBB for a triangular mesh. - * \param tris [in] list of triangles - * \param nbtris [in] number of triangles - * \param verts [in] list of vertices - * \param nbverts [in] number of vertices - * \param obb [out] the obb - * \param usehull [in] true to compute the OBB of the mesh's convex hull - * \return true if success - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FUNCTION MESHMERIZER_API bool ComputeOBB(const IndexedTriangle* tris, udword nbtris, const Point* verts, udword nbverts, OBB& obb, bool usehull); - - FUNCTION MESHMERIZER_API void OBBFromTriangle(const Point& p1, const Point& p2, const Point& p3, OBB& obb); - -#endif // __ICEOBB_H__ diff --git a/OpcodeDistrib/OPC_OptimizedTree.h b/OpcodeDistrib/OPC_OptimizedTree.h deleted file mode 100644 index e083f33..0000000 --- a/OpcodeDistrib/OPC_OptimizedTree.h +++ /dev/null @@ -1,171 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for optimized trees. - * \file OPC_OptimizedTree.h - * \author Pierre Terdiman - * \date March, 20, 2001 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __OPC_OPTIMIZEDTREE_H__ -#define __OPC_OPTIMIZEDTREE_H__ - - //! Common interface for a node of an implicit tree - #define IMPLEMENT_IMPLICIT_NODE(baseclass, volume) \ - public: \ - /* Constructor / Destructor */ \ - inline_ baseclass() : mData(0) {} \ - inline_ ~baseclass() {} \ - /* Leaf test */ \ - inline_ BOOL IsLeaf() const { return mData&1; } \ - /* Data access */ \ - inline_ const baseclass* GetPos() const { return (baseclass*)mData; } \ - inline_ const baseclass* GetNeg() const { return ((baseclass*)mData)+1; } \ - inline_ udword GetPrimitive() const { return (mData>>1); } \ - /* Stats */ \ - inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ - \ - volume mAABB; \ - udword mData; - - //! Common interface for a node of a no-leaf tree - #define IMPLEMENT_NOLEAF_NODE(baseclass, volume) \ - public: \ - /* Constructor / Destructor */ \ - inline_ baseclass() : mData(0), mData2(0) {} \ - inline_ ~baseclass() {} \ - /* Leaf tests */ \ - inline_ BOOL HasLeaf() const { return mData&1; } \ - inline_ BOOL HasLeaf2() const { return mData2&1; } \ - /* Data access */ \ - inline_ const baseclass* GetPos() const { return (baseclass*)mData; } \ - inline_ const baseclass* GetNeg() const { return (baseclass*)mData2; } \ - inline_ udword GetPrimitive() const { return (mData>>1); } \ - inline_ udword GetPrimitive2() const { return (mData2>>1); } \ - /* Stats */ \ - inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ - \ - volume mAABB; \ - udword mData; \ - udword mData2; - - class OPCODE_API AABBCollisionNode - { - IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB) - - inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; } - inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); } - inline_ udword GetRadius() const - { - udword* Bits = (udword*)&mAABB.mExtents.x; - udword Max = Bits[0]; - if(Bits[1]>Max) Max = Bits[1]; - if(Bits[2]>Max) Max = Bits[2]; - return Max; - } - - // NB: using the square-magnitude or the true volume of the box, seems to yield better results - // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size" - // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's - // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is - // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices - // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very - // good strategy. - }; - - class OPCODE_API AABBQuantizedNode - { - IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB) - - inline_ uword GetSize() const - { - const uword* Bits = mAABB.mExtents; - uword Max = Bits[0]; - if(Bits[1]>Max) Max = Bits[1]; - if(Bits[2]>Max) Max = Bits[2]; - return Max; - } - // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all - // over the place.......! - }; - - class OPCODE_API AABBNoLeafNode - { - IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB) - }; - - class OPCODE_API AABBQuantizedNoLeafNode - { - IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB) - }; - - //! Common interface for a collision tree - #define IMPLEMENT_COLLISION_TREE(baseclass, volume) \ - public: \ - /* Constructor / Destructor */ \ - baseclass(); \ - virtual ~baseclass(); \ - /* Build from a standard tree */ \ - virtual bool Build(AABBTree* tree); \ - /* Data access */ \ - inline_ const volume* GetNodes() const { return mNodes; } \ - /* Stats */ \ - virtual udword GetUsedBytes() const { return mNbNodes*sizeof(volume); } \ - private: \ - volume* mNodes; - - class OPCODE_API AABBOptimizedTree - { - public: - // Constructor / Destructor - AABBOptimizedTree() : mNbNodes(0) {} - virtual ~AABBOptimizedTree() {} - - // Data access - inline_ udword GetNbNodes() const { return mNbNodes; } - - virtual udword GetUsedBytes() const = 0; - virtual bool Build(AABBTree* tree) = 0; - protected: - udword mNbNodes; - }; - - class OPCODE_API AABBCollisionTree : public AABBOptimizedTree - { - IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode) - }; - - class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree - { - IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode) - }; - - class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree - { - IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode) - - public: - Point mCenterCoeff; - Point mExtentsCoeff; - }; - - class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree - { - IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode) - - public: - Point mCenterCoeff; - Point mExtentsCoeff; - }; - -#endif // __OPC_OPTIMIZEDTREE_H__ diff --git a/OpcodeDistrib/OPC_PlanesCollider.cpp b/OpcodeDistrib/OPC_PlanesCollider.cpp deleted file mode 100644 index 3a57f39..0000000 --- a/OpcodeDistrib/OPC_PlanesCollider.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for a planes collider. - * \file OPC_PlanesCollider.cpp - * \author Pierre Terdiman - * \date January, 1st, 2002 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -using namespace Opcode; - -#include "OPC_PlanesAABBOverlap.h" -#include "OPC_PlanesTriOverlap.h" - -//! Planes-triangle test -#ifdef OPC_USE_CALLBACKS - #define PLANES_PRIM(primindex) \ - /* Request vertices from the app */ \ - (mObjCallback)(primindex, mVP, mUserData); \ - /* Perform triangle-box overlap test */ \ - if(PlanesTriOverlap(clipmask)) \ - { \ - /* Set contact status */ \ - mFlags |= OPC_CONTACT; \ - mTouchedPrimitives->Add(primindex); \ - } -#else - #define PLANES_PRIM(primindex) \ - /* Direct access to vertices */ \ - const IndexedTriangle* T = &mFaces[primindex]; \ - mVP.Vertex[0] = &mVerts[T->mVRef[0]]; \ - mVP.Vertex[1] = &mVerts[T->mVRef[1]]; \ - mVP.Vertex[2] = &mVerts[T->mVRef[2]]; \ - /* Perform triangle-box overlap test */ \ - if(PlanesTriOverlap(clipmask)) \ - { \ - /* Set contact status */ \ - mFlags |= OPC_CONTACT; \ - mTouchedPrimitives->Add(primindex); \ - } -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Constructor. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -PlanesCollider::PlanesCollider() : - mPlanes (null), - mNbPlanes (0) -{ -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Destructor. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -PlanesCollider::~PlanesCollider() -{ - DELETEARRAY(mPlanes); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Validates current settings. You should call this method after all the settings and callbacks have been defined. - * \return null if everything is ok, else a string describing the problem - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const char* PlanesCollider::ValidateSettings() -{ - if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; - - return VolumeCollider::ValidateSettings(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Generic collision query for generic OPCODE models. After the call, access the results: - * - with GetContactStatus() - * - with GetNbTouchedFaces() - * - with GetTouchedFaces() - * - * \param cache [in/out] a planes cache - * \param planes [in] list of planes in world space - * \param nb_planes [in] number of planes - * \param model [in] Opcode model to collide with - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, OPCODE_Model* model, const Matrix4x4* worldm) -{ - // Checkings - if(!planes || !model) return false; - - // Simple double-dispatch - if(!model->HasLeafNodes()) - { - if(model->IsQuantized()) return Collide(cache, planes, nb_planes, (const AABBQuantizedNoLeafTree*)model->GetTree(), worldm); - else return Collide(cache, planes, nb_planes, (const AABBNoLeafTree*)model->GetTree(), worldm); - } - else - { - if(model->IsQuantized()) return Collide(cache, planes, nb_planes, (const AABBQuantizedTree*)model->GetTree(), worldm); - else return Collide(cache, planes, nb_planes, (const AABBCollisionTree*)model->GetTree(), worldm); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Initializes a collision query : - * - reset stats & contact status - * - compute planes in model space - * - check temporal coherence - * - * \param cache [in/out] a planes cache - * \param planes [in] list of planes - * \param nb_planes [in] number of planes - * \param worldm [in] model's world matrix, or null - * \return contact status - * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -BOOL PlanesCollider::InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm) -{ - // 1) Call the base method - VolumeCollider::InitQuery(); - - // 2) Compute planes in model space - if(nb_planes>mNbPlanes) - { - DELETEARRAY(mPlanes); - mPlanes = new Plane[nb_planes]; - } - mNbPlanes = nb_planes; - - if(worldm) - { - Matrix4x4 InvWorldM; - InvertPRMatrix(InvWorldM, *worldm); - -// for(udword i=0;i check results from previous frame before performing the collision query - if(FirstContactEnabled()) - { - // We're only interested in the first contact found => test the unique previously touched face - if(mTouchedPrimitives->GetNbEntries()) - { - // Get index of previously touched face = the first entry in the array - udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); - - // Then reset the array: - // - if the overlap test below is successful, the index we'll get added back anyway - // - if it isn't, then the array should be reset anyway for the normal query - mTouchedPrimitives->Reset(); - - // Perform overlap test between the cached triangle and the planes (and set contact status if needed) - udword clipmask = (1< we'll have to perform a normal query - } - else mTouchedPrimitives->Reset(); - } - else - { - // Here we don't use temporal coherence => do a normal query - mTouchedPrimitives->Reset(); - } - - return GetContactStatus(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Collision query for normal trees. - * \param cache [in/out] a planes cache - * \param planes [in] list of planes - * \param nb_planes [in] number of planes - * \param tree [in] model's AABB tree - * \param worldm [in] model's world matrix, or null - * \return true if success - * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const AABBCollisionTree* tree, const Matrix4x4* worldm) -{ - // Checkings - if(!tree || !planes || !nb_planes) return false; -#ifdef OPC_USE_CALLBACKS - if(!mObjCallback) return false; -#else - if(!mFaces || !mVerts) return false; -#endif - - // Init collision query - if(InitQuery(cache, planes, nb_planes, worldm)) return true; - - // Perform collision query - _Collide(tree->GetNodes(), (1<GetNodes(), (1<mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes(), (1<mCenterCoeff; - mExtentsCoeff = tree->mExtentsCoeff; - - // Perform collision query - _Collide(tree->GetNodes(), (1<mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clipmask)) return; - - // If the box is completely included, so are its children. We don't need to do extra tests, we - // can immediately output a list of visible children. Those ones won't need to be clipped. - if(!OutClipMask) - { - // Set contact status - mFlags |= OPC_CONTACT; - _Dump(node); - return; - } - - // Else the box straddles one or several planes, so we need to recurse down the tree. - - if(node->IsLeaf()) - { - PLANES_PRIM(node->GetPrimitive()) - } - else - { - _Collide(node->GetPos(), OutClipMask); - - if(ContactFound()) return; - - _Collide(node->GetNeg(), OutClipMask); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Recursive collision query for quantized AABB trees. - * \param node [in] current collision node - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void PlanesCollider::_Collide(const AABBQuantizedNode* node, udword clipmask) -{ - // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); - - // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. - udword OutClipMask; - if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clipmask)) return; - - // If the box is completely included, so are its children. We don't need to do extra tests, we - // can immediately output a list of visible children. Those ones won't need to be clipped. - if(!OutClipMask) - { - // Set contact status - mFlags |= OPC_CONTACT; - _Dump(node); - return; - } - - // Else the box straddles one or several planes, so we need to recurse down the tree. - - if(node->IsLeaf()) - { - PLANES_PRIM(node->GetPrimitive()) - } - else - { - _Collide(node->GetPos(), OutClipMask); - - if(ContactFound()) return; - - _Collide(node->GetNeg(), OutClipMask); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Recursive collision query for no-leaf AABB trees. - * \param node [in] current collision node - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void PlanesCollider::_Collide(const AABBNoLeafNode* node, udword clipmask) -{ - // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. - udword OutClipMask; - if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clipmask)) return; - - // If the box is completely included, so are its children. We don't need to do extra tests, we - // can immediately output a list of visible children. Those ones won't need to be clipped. - if(!OutClipMask) - { - // Set contact status - mFlags |= OPC_CONTACT; - _Dump(node); - return; - } - - // Else the box straddles one or several planes, so we need to recurse down the tree. - - if(node->HasLeaf()) { PLANES_PRIM(node->GetPrimitive()) } - else _Collide(node->GetPos(), OutClipMask); - - if(ContactFound()) return; - - if(node->HasLeaf2()) { PLANES_PRIM(node->GetPrimitive2()) } - else _Collide(node->GetNeg(), OutClipMask); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Recursive collision query for quantized no-leaf AABB trees. - * \param node [in] current collision node - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void PlanesCollider::_Collide(const AABBQuantizedNoLeafNode* node, udword clipmask) -{ - // Dequantize box - const QuantizedAABB* Box = &node->mAABB; - const Point Center(float(Box->mCenter[0]) * mCenterCoeff.x, float(Box->mCenter[1]) * mCenterCoeff.y, float(Box->mCenter[2]) * mCenterCoeff.z); - const Point Extents(float(Box->mExtents[0]) * mExtentsCoeff.x, float(Box->mExtents[1]) * mExtentsCoeff.y, float(Box->mExtents[2]) * mExtentsCoeff.z); - - // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. - udword OutClipMask; - if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clipmask)) return; - - // If the box is completely included, so are its children. We don't need to do extra tests, we - // can immediately output a list of visible children. Those ones won't need to be clipped. - if(!OutClipMask) - { - // Set contact status - mFlags |= OPC_CONTACT; - _Dump(node); - return; - } - - // Else the box straddles one or several planes, so we need to recurse down the tree. - - if(node->HasLeaf()) { PLANES_PRIM(node->GetPrimitive()) } - else _Collide(node->GetPos(), OutClipMask); - - if(ContactFound()) return; - - if(node->HasLeaf2()) { PLANES_PRIM(node->GetPrimitive2()) } - else _Collide(node->GetNeg(), OutClipMask); -} diff --git a/OpcodeDistrib/OPC_TreeBuilders.h b/OpcodeDistrib/OPC_TreeBuilders.h deleted file mode 100644 index c79f446..0000000 --- a/OpcodeDistrib/OPC_TreeBuilders.h +++ /dev/null @@ -1,137 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for tree builders. - * \file OPC_TreeBuilders.h - * \author Pierre Terdiman - * \date March, 20, 2001 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __OPC_TREEBUILDERS_H__ -#define __OPC_TREEBUILDERS_H__ - - //! Tree splitting rules - enum SplittingRules - { - // Tree - SPLIT_COMPLETE = (1<<0), //!< Build a complete tree (2*N-1 nodes) - // Primitive split - SPLIT_LARGESTAXIS = (1<<1), //!< Split along the largest axis - SPLIT_SPLATTERPOINTS = (1<<2), //!< Splatter primitive centers (QuickCD-style) - SPLIT_BESTAXIS = (1<<3), //!< Try largest axis, then second, then last - SPLIT_BALANCED = (1<<4), //!< Try to keep a well-balanced tree - SPLIT_FIFTY = (1<<5), //!< Arbitrary 50-50 split - // Node split - SPLIT_GEOMCENTER = (1<<6), //!< Split at geometric center (else split in the middle) - // - SPLIT_FORCE_DWORD = 0x7fffffff - }; - - class OPCODE_API AABBTreeBuilder - { - public: - //! Constructor - AABBTreeBuilder() : - mLimit(0), - mRules(SPLIT_FORCE_DWORD), - mNbPrimitives(0), - mCount(0), - mNbInvalidSplits(0) {} - //! Destructor - virtual ~AABBTreeBuilder() {} - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes the AABB of a set of primitives. - * \param primitives [in] list of indices of primitives - * \param nb_prims [in] number of indices - * \param global_box [out] global AABB enclosing the set of input primitives - * \return true if success - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - virtual bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const = 0; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes the splitting value along a given axis for a given primitive. - * \param index [in] index of the primitive to split - * \param axis [in] axis index (0,1,2) - * \return splitting value - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - virtual float GetSplittingValue(udword index, udword axis) const = 0; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Computes the splitting value along a given axis for a given node. - * \param primitives [in] list of indices of primitives - * \param nb_prims [in] number of indices - * \param global_box [in] global AABB enclosing the set of input primitives - * \param axis [in] axis index (0,1,2) - * \return splitting value - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - virtual float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const - { - // Default split value = middle of the axis (using only the box) - return global_box.GetCenter(axis); - } - - udword mLimit; //!< Limit number of primitives / node - udword mRules; //!< Building/Splitting rules (a combination of flags) - udword mNbPrimitives; //!< Total number of primitives. - // Stats - inline_ void SetCount(udword nb) { mCount=nb; } - inline_ void IncreaseCount(udword nb) { mCount+=nb; } - inline_ udword GetCount() const { return mCount; } - inline_ void SetNbInvalidSplits(udword nb) { mNbInvalidSplits=nb; } - inline_ void IncreaseNbInvalidSplits() { mNbInvalidSplits++; } - inline_ udword GetNbInvalidSplits() const { return mNbInvalidSplits; } - - private: - udword mCount; //!< Stats: number of nodes created - udword mNbInvalidSplits; //!< Stats: number of invalid splits - }; - - class OPCODE_API AABBTreeOfAABBsBuilder : public AABBTreeBuilder - { - public: - //! Constructor - AABBTreeOfAABBsBuilder() : mAABBList(null) {} - //! Destructor - virtual ~AABBTreeOfAABBsBuilder() {} - - override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const; - override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const; - - const AABB* mAABBList; //!< Shortcut to an app-controlled list of AABBs. - }; - - class OPCODE_API AABBTreeOfTrianglesBuilder : public AABBTreeBuilder - { - public: - //! Constructor - AABBTreeOfTrianglesBuilder() : mTriList(null), mVerts(null), mNbTriangles(0) {} - //! Destructor - virtual ~AABBTreeOfTrianglesBuilder() {} - - override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const; - override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const; - override(AABBTreeBuilder) float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const; - - const IndexedTriangle* mTriList; //!< Shortcut to an app-controlled list of triangles. - const Point* mVerts; //!< Shortcut to an app-controlled list of vertices. - const udword mNbTriangles; //!< Total number of triangles. - }; - -#endif // __OPC_TREEBUILDERS_H__ diff --git a/OpcodeDistrib/OPC_Triangle.h b/OpcodeDistrib/OPC_Triangle.h deleted file mode 100644 index 0af7305..0000000 --- a/OpcodeDistrib/OPC_Triangle.h +++ /dev/null @@ -1,39 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains a handy triangle class. - * \file IceTriangle.h - * \author Pierre Terdiman - * \date January, 17, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICETRIANGLE_H__ -#define __ICETRIANGLE_H__ - - // An indexed triangle class. - class MESHMERIZER_API IndexedTriangle - { - public: - //! Constructor - inline_ IndexedTriangle() {} - //! Constructor - inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; } - //! Copy constructor - inline_ IndexedTriangle(const IndexedTriangle& triangle) - { - mVRef[0] = triangle.mVRef[0]; - mVRef[1] = triangle.mVRef[1]; - mVRef[2] = triangle.mVRef[2]; - } - //! Destructor - inline_ ~IndexedTriangle() {} - //! Vertex-references - udword mVRef[3]; - - // Methods - bool IsDegenerate() const; - }; - -#endif // __ICETRIANGLE_H__ diff --git a/OpcodeDistrib/OPC_Types.h b/OpcodeDistrib/OPC_Types.h deleted file mode 100644 index 673bce4..0000000 --- a/OpcodeDistrib/OPC_Types.h +++ /dev/null @@ -1,147 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains custom types. - * \file IceTypes.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICETYPES_H__ -#define __ICETYPES_H__ - - // Constants - #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI - #define HALFPI 1.57079632679489661923f //!< 0.5 * PI - #define TWOPI 6.28318530717958647692f //!< 2.0 * PI - #define INVPI 0.31830988618379067154f //!< 1.0 / PI - - #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees - #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians - - #define EXP 2.71828182845904523536f //!< e - #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2) - #define LN2 0.693147180559945f //!< ln(2) - #define INVLN2 1.44269504089f //!< 1.0f / ln(2) - - #define INV3 0.33333333333333333333f //!< 1/3 - #define INV6 0.16666666666666666666f //!< 1/6 - #define INV7 0.14285714285714285714f //!< 1/7 - #define INV9 0.11111111111111111111f //!< 1/9 - #define INV255 0.00392156862745098039f //!< 1/255 - - #define null 0 //!< our own NULL pointer - - // New types - typedef signed char sbyte; //!< sizeof(sbyte) must be 1 - typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 - typedef signed short sword; //!< sizeof(sword) must be 2 - typedef unsigned short uword; //!< sizeof(uword) must be 2 - typedef signed int sdword; //!< sizeof(sdword) must be 4 - typedef unsigned int udword; //!< sizeof(udword) must be 4 - typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 - typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 - typedef float float32; //!< sizeof(float32) must be 4 - typedef double float64; //!< sizeof(float64) must be 4 - -#define ICE_COMPILE_TIME_ASSERT(name, x) typedef int ICE_Dummy_ ## name[(x) * 2 - 1] - - ICE_COMPILE_TIME_ASSERT(bool, sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 ! - ICE_COMPILE_TIME_ASSERT(ubyte, sizeof(ubyte)==1); - ICE_COMPILE_TIME_ASSERT(sbyte, sizeof(sbyte)==1); - ICE_COMPILE_TIME_ASSERT(sword, sizeof(sword)==2); - ICE_COMPILE_TIME_ASSERT(uword, sizeof(uword)==2); - ICE_COMPILE_TIME_ASSERT(udword, sizeof(udword)==4); - ICE_COMPILE_TIME_ASSERT(sdword, sizeof(sdword)==4); - ICE_COMPILE_TIME_ASSERT(uqword, sizeof(uqword)==8); - ICE_COMPILE_TIME_ASSERT(sqword, sizeof(sqword)==8); - -#undef ICE_COMPILE_TIME_ASSERT - - typedef udword DynID; //!< Dynamic identifier - typedef uword KID; //!< Kernel ID - typedef udword RTYPE; //!< Relationship-type (!) between owners and references - #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers) - #define INVALID_KID 0xffff //!< Invalid Kernel ID - #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value - - // Define BOOL if needed - #ifndef BOOL - typedef int BOOL; //!< Another boolean type. - #endif - - //! Union of a float and a sdword - typedef union { - float f; //!< The float - sdword d; //!< The integer - }scell; - - //! Union of a float and a udword - typedef union { - float f; //!< The float - udword d; //!< The integer - }ucell; - - //! A generic couple structure - struct Pair - { - udword id0; //!< First index of the pair - udword id1; //!< Second index of the pair - }; - - // Type ranges - #define MAX_SBYTE 0x7f //!< max possible sbyte value - #define MIN_SBYTE 0x80 //!< min possible sbyte value - #define MAX_UBYTE 0xff //!< max possible ubyte value - #define MIN_UBYTE 0x00 //!< min possible ubyte value - #define MAX_SWORD 0x7fff //!< max possible sword value - #define MIN_SWORD 0x8000 //!< min possible sword value - #define MAX_UWORD 0xffff //!< max possible uword value - #define MIN_UWORD 0x0000 //!< min possible uword value - #define MAX_SDWORD 0x7fffffff //!< max possible sdword value - #define MIN_SDWORD 0x80000000 //!< min possible sdword value - #define MAX_UDWORD 0xffffffff //!< max possible udword value - #define MIN_UDWORD 0x00000000 //!< min possible udword value - #define MAX_FLOAT FLT_MAX //!< max possible float value - #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value - #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 - #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0 - #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT - #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT - - #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand() - - typedef int (__stdcall* PROC)(); //!< A standard procedure call. - typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call - typedef void** VTABLE; //!< A V-Table. - - #undef MIN - #undef MAX - #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b - #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b - #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c - - template inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; } - template inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; } - template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } - template inline_ void TSetMax (T& a, const T& b) { if(a RayCollider, AABBSphereCollider => SphereCollider - - the sphere query now only returns a list of faces (extra info discarded). On the other hand it's a lot faster. - - OBB, AABB and planes queries. Original OBB and AABB queries contributed by Erwin de Vries. - - cosmetic changes in OPC_BoxBoxOverlap.h contributed by Gottfried Chen - - some inlining problems fixed - - faster ray-mesh tests using the separating axis theorem - - new split value in AABB tree construction (contributed by Igor Kravtchenko). Provides faster queries most of the time. - - improved temporal coherence for sphere & AABB queries (works in "All contacts" mode) - - Notes: - - Everything in the "Ice code" directory (in VC++) is basically copy-pasted from my engine, with a lot - of code removed until there was no link error anymore. Don't expect those files to be cute or anything, - they've never been meant to be released and they're often updated/modified/messy. - - Some experimental features have been removed as well. Else I would never have released the 1.2... - - Not as polished/optimal as I would like it to be, but that's life. I promised myself to release it - before october 2002 (one YEAR later ?!).... That's the only reason why it's there. - - Some people reported ColDet was faster. Uh, come on. They were using Opcode in - "All contacts" mode whereas ColDet was doing "first contact"... - - OPCODE distribution 1.1 (october 2001) - ----------------------- - - New in Opcode 1.1: - - stabbing queries - - sphere queries - - abtract base class for colliders - - settings validation methods - - compilation flags now grouped in OPC_Settings.h - - smaller files, new VC++ virtual dirs (cleaner) - - Notes: - - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info. - - I code in 1600*1200, so some lines may look a bit long.. - - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries - can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB - code, but the newer one seems better. Tim Schröder's one is good as well. See: www.codercorner.com/RayAABB.cpp - - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!) - - I removed various tests before releasing this one: - - a separation line, a.k.a. "front" in QuickCD, because gains were unclear - - distance queries in a PQP style, because it was way too slow - - support for deformable models, too slow as well - - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way. - If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current - demo uses copyrighted art I'm not allowed to spread) - - Sorry for the lack of real docs and/or solid examples. I just don't have enough time. - - OPCODE distribution 1.0 (march 2001) - ----------------------- - - - First release - - =============================================================================== - - WHAT ? - - OPCODE means OPtimized COllision DEtection. - So this is a collision detection package similar to RAPID. Here's a - quick list of features: - - - C++ interface, developed for Windows systems using VC++ 6.0 - - Works on arbitrary meshes (convex or non-convex), even polygon soups - - Current implementation uses AABB-trees - - Supports CD queries for 2 bodies only. The sweep-and-prune and N-body code is part - of a bigger unreleased package (Z-COLLIDE) - - Introduces Primitive-BV overlap tests during recursive collision queries (whereas - standard libraries only rely on Primitive-Primitive and BV-BV tests) - - Introduces no-leaf trees, i.e. collision trees whose leaf nodes have been removed - - Supports collision queries on quantized trees (decompressed on-the-fly) - - Supports "first contact" or "all contacts" modes (ā la RAPID) - - Uses temporal coherence for "first contact" mode (~10 to 20 times faster, useful - in rigid body simulation during bisection) - - Limited support for temporal coherence in "all contacts" mode. This provides O(1) - queries returning a set of potentially collided faces. - - Memory footprint is 7.2 times smaller than RAPID's one, which is ideal for console - games with limited ram (actually, if you use the unmodified RAPID code using double - precision, it's more like 13 times smaller...) - - And yet it often runs faster than RAPID (according to RDTSC, sometimes more than 5 - times faster when objects are deeply overlapping) - - Performance is usually close to RAPID's one in close-proximity situations - - Stabbing queries - - Sphere queries - - Box queries (AABB/OBB) - - Planes queries - - Still does not work on deformable meshes. - - What it can be used for: - - standard mesh-mesh collision detection (similar to RAPID, SOLID, QuickCD, PQP, ColDet...) - - camera-vs-world collisions (similar to Telemachos/Paul Nettle/Stan Melax articles) - - shadow feelers to speed up lightmap computations - - in-out tests to speed up voxelization processes - - etc... - - WHY ? - - - Because RAPID uses too many bytes. - - Because the idea was nice... - - WHEN ? - - It's been coded in march 2001 following a thread on the GD-Algorithms list. - - GDAlgorithms-list mailing list - GDAlgorithms-list@lists.sourceforge.net - http://lists.sourceforge.net/lists/listinfo/gdalgorithms-list - - WHO ? - - Pierre Terdiman - August, 1, 2002 - - p.terdiman@wanadoo.fr - p.terdiman@codercorner.com - - http://www.codercorner.com - http://www.codercorner.com/Opcode.htm diff --git a/ReadMe.txt b/ReadMe.txt index 002a8e5..ee16382 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -1,30 +1,109 @@ - OPCODE distribution 1.1 + OPCODE distribution 1.3 (june 2003) ----------------------- - New in Opcode 1.1: - - stabbing queries - - sphere queries - - abtract base class for colliders - - settings validation methods - - compilation flags now grouped in OPC_Settings.h - - smaller files, new VC++ virtual dirs (cleaner) - - Notes: - - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info. - - I code in 1600*1200, so some lines may look a bit long.. - - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries - can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB - code, but the newer one seems better. Tim Schröder's one is good as well. See: www.codercorner.com/RayAABB.cpp - - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!) - - I removed various tests before releasing this one: - - a separation line, a.k.a. "front" in QuickCD, because gains were unclear - - distance queries in a PQP style, because it was way too slow - - support for deformable models, too slow as well - - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way. - If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current - demo uses copyrighted art I'm not allowed to spread) - - Sorry for the lack of real docs and/or solid examples. I just don't have enough time. + New in Opcode 1.3: + - fixed the divide by 0 bug that was happening when all centers where located on a coordinate axis (thanks to Jorrit T) + - linearized "complete" vanilla AABB trees + - ANSI-compliant "for" loops (for the ones porting it to Linux...) + - callbacks & pointers moved to mesh interface + - support for triangle & vertex strides + - optimized the sphere-triangle overlap code a bit + - dynamic trees (refit) + - more builders + - ValidateSubdivision in builders + - LSS collider + - primitive-bv tests can now be skipped in most volume queries + - temporal coherence now also works for airborne objects + - temporal coherence completed for boxes / all contacts, LSS, etc + - ray-collider now uses a callback + - some common "usages" have been introduced (only picking for now) + - SPLIT_COMPLETE removed (now implicitely using mLimit = 1) + - hybrid collision models + - sweep-and-prune code added, moved from my old Z-Collide lib + - it now works with meshes made of only 1 triangle (except in mesh-mesh case!) + + Disclaimer: + + - I forced myself to actually *do* the release today no matter what. Else it would never have been done. That's + why the code may not be very polished. I also removed a *lot* of things (more usages, distance queries, etc...) + that weren't ready for prime-time (or that were linked to too many of my supporting libs) + + - Some comments may also be obsolete here and there. The old User Manual for Opcode 1.2 may not fit version 1.3 + either, since there's a new "mesh interface" to support strides, etc. + + - Everything in the "Ice" directory has been hacked out of my engine and edited until everything compiled. Don't + expect anything out there to be cute or something. In particular, some CPP files are not even included when not + needed, so you can expect some linker errors if you try messing around with them... + + Otherwise, it should be just like previous version, only better. In particular, hybrid models can be very + memory-friendly (sometimes using like 10 times less ram than the best trees from version 1.2). The possible + speed hit is often invisible (if it even exists), especially using temporal coherence in "all contacts" mode. + (Admittedly, this depends on your particular usage pattern / what you do on collided triangles). + + The sweep-and-prune code is similar to the "vanilla" version found in V-Collide (but that one's better IMHO...) + The simple "radix" version is often just as good, see for yourself. + + OPCODE distribution 1.2 (august 2002) + ----------------------- + + New in Opcode 1.2: + - new VolumeCollider base class + - simplified callback setup + - you can now use callbacks or pointers (setup at compile time) + - destination array not needed anymore in the RayCollider (faster in-out tests) + - renamed classes: AABBRayCollider => RayCollider, AABBSphereCollider => SphereCollider + - the sphere query now only returns a list of faces (extra info discarded). On the other hand it's a lot faster. + - OBB, AABB and planes queries. Original OBB and AABB queries contributed by Erwin de Vries. + - cosmetic changes in OPC_BoxBoxOverlap.h contributed by Gottfried Chen + - some inlining problems fixed + - faster ray-mesh tests using the separating axis theorem + - new split value in AABB tree construction (contributed by Igor Kravtchenko). Provides faster queries most of the time. + - improved temporal coherence for sphere & AABB queries (works in "All contacts" mode) + + Notes: + + - Everything in the "Ice code" directory (in VC++) is basically copy-pasted from my engine, with a lot + of code removed until there was no link error anymore. Don't expect those files to be cute or anything, + they've never been meant to be released and they're often updated/modified/messy. + - Some experimental features have been removed as well. Else I would never have released the 1.2... + - Not as polished/optimal as I would like it to be, but that's life. I promised myself to release it + before october 2002 (one YEAR later ?!).... That's the only reason why it's there. + - Some people reported ColDet was faster. Uh, come on. They were using Opcode in + "All contacts" mode whereas ColDet was doing "first contact"... + + OPCODE distribution 1.1 (october 2001) + ----------------------- + + New in Opcode 1.1: + - stabbing queries + - sphere queries + - abtract base class for colliders + - settings validation methods + - compilation flags now grouped in OPC_Settings.h + - smaller files, new VC++ virtual dirs (cleaner) + + Notes: + + - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info. + - I code in 1600*1200, so some lines may look a bit long.. + - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries + can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB + code, but the newer one seems better. Tim Schröder's one is good as well. See: www.codercorner.com/RayAABB.cpp + - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!) + - I removed various tests before releasing this one: + - a separation line, a.k.a. "front" in QuickCD, because gains were unclear + - distance queries in a PQP style, because it was way too slow + - support for deformable models, too slow as well + - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way. + If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current + demo uses copyrighted art I'm not allowed to spread) + - Sorry for the lack of real docs and/or solid examples. I just don't have enough time. + + OPCODE distribution 1.0 (march 2001) + ----------------------- + + - First release =============================================================================== @@ -37,8 +116,6 @@ - C++ interface, developed for Windows systems using VC++ 6.0 - Works on arbitrary meshes (convex or non-convex), even polygon soups - Current implementation uses AABB-trees - - Supports CD queries for 2 bodies only. The sweep-and-prune and N-body code is part - of a bigger unreleased package (Z-COLLIDE) - Introduces Primitive-BV overlap tests during recursive collision queries (whereas standard libraries only rely on Primitive-Primitive and BV-BV tests) - Introduces no-leaf trees, i.e. collision trees whose leaf nodes have been removed @@ -52,8 +129,22 @@ - And yet it often runs faster than RAPID (according to RDTSC, sometimes more than 5 times faster when objects are deeply overlapping) - Performance is usually close to RAPID's one in close-proximity situations - - Stabbing & sphere queries - - Does not work on deformable meshes. Yet. + - Stabbing, planes & volume queries (sphere, AABB, OBB, LSS) + - Sweep-and-prune + - Now works with deformable meshes + - Hybrid trees + + + What it can be used for: + - standard mesh-mesh collision detection (similar to RAPID, SOLID, QuickCD, PQP, ColDet...) + - N-body collisions (similar to V-Collide) + - camera-vs-world collisions (similar to Telemachos/Paul Nettle/Stan Melax articles) + - shadow feelers to speed up lightmap computations + - in-out tests to speed up voxelization processes + - picking + - rigid body simulation + - view frustum culling + - etc WHY ? @@ -71,7 +162,7 @@ WHO ? Pierre Terdiman - October 9, 2001 + June, 1, 2003 p.terdiman@wanadoo.fr p.terdiman@codercorner.com diff --git a/OpcodeDistrib/StdAfx.cpp b/StdAfx.cpp similarity index 95% rename from OpcodeDistrib/StdAfx.cpp rename to StdAfx.cpp index 4bb29c3..c9b75bb 100644 --- a/OpcodeDistrib/StdAfx.cpp +++ b/StdAfx.cpp @@ -6,5 +6,5 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#define ICE_MAIN +//#define ICE_MAIN #include "Stdafx.h" diff --git a/OpcodeDistrib/StdAfx.h b/StdAfx.h similarity index 85% rename from OpcodeDistrib/StdAfx.h rename to StdAfx.h index 6d4dabf..9988c25 100644 --- a/OpcodeDistrib/StdAfx.h +++ b/StdAfx.h @@ -16,15 +16,6 @@ // Insert your headers here #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -//#define OPCODE_USING_ICE - -#ifdef OPCODE_USING_ICE - - #include - using namespace ZCollide; - -#endif // OPCODE_USING_ICE - #include "Opcode.h" //{{AFX_INSERT_LOCATION}} diff --git a/TemporalCoherence.txt b/TemporalCoherence.txt new file mode 100644 index 0000000..8fde158 --- /dev/null +++ b/TemporalCoherence.txt @@ -0,0 +1,32 @@ + +> Hi John, +> +> I know I'll forget to tell you this if I don't write it right now.... +> +> >(2) How is the receiving geometry for the shadow decided? +> +> I wrote about an LSS-test but actually performing a new VFC test (from the +> light's view) is the same. In both cases, here's a trick to take advantage +> of temporal coherence : test the world against a slightly larger than +> necessary LSS or frustum. Keep the list of touched surfaces. Then next +> frame, if the new volume is still contained within the previous one used +for +> the query, you can reuse the same list immediately. Actually it's a bit +> similar to what you did in your sphere-tree, I think. Anyway, now the +O(log +> N) VFC is O(1) for some frames. It's not worth it for the "real" VFC, but +> when you have N virtual frustum to test to drop N shadows, that's another +> story. +> +> Two downsides: +> - You need more ram to keep track of one list of meshes / shadow, but +> usually it's not a lot. +> - By using a larger volume for the query you possibly touch more +> faces/surfaces, which will be rendered in the shadow pass. Usually it's +not +> a problem either since rendering is simply faster than geometric queries +> those days. But of course, "your mileage may vary". +> +> Happy new year ! +> +> Pierre diff --git a/TestOpcode/IceBunny.cpp b/TestOpcode/IceBunny.cpp deleted file mode 100644 index 5f0671d..0000000 --- a/TestOpcode/IceBunny.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains a bunny object. - * \file IceBunny.cpp - * \author Pierre Terdiman - * \date January, 17, 1999 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Precompiled Header -#include "Stdafx.h" - -#pragma warning(disable : 4305) -#pragma warning(disable : 4136) - -#define BUNNY_NBVERTICES (453) -#define BUNNY_NBFACES (902) - -float BunnyVertices[BUNNY_NBVERTICES][3]={ - {-0.334392,0.133007,0.062259}, - {-0.350189,0.150354,-0.147769}, - {-0.234201,0.343811,-0.174307}, - {-0.200259,0.285207,0.093749}, - {0.003520,0.475208,-0.159365}, - {0.001856,0.419203,0.098582}, - {-0.252802,0.093666,0.237538}, - {-0.162901,0.237984,0.206905}, - {0.000865,0.318141,0.235370}, - {-0.414624,0.164083,-0.278254}, - {-0.262213,0.357334,-0.293246}, - {0.004628,0.482694,-0.338626}, - {-0.402162,0.133528,-0.443247}, - {-0.243781,0.324275,-0.436763}, - {0.005293,0.437592,-0.458332}, - {-0.339884,-0.041150,-0.668211}, - {-0.248382,0.255825,-0.627493}, - {0.006261,0.376103,-0.631506}, - {-0.216201,-0.126776,-0.886936}, - {-0.171075,0.011544,-0.881386}, - {-0.181074,0.098223,-0.814779}, - {-0.119891,0.218786,-0.760153}, - {-0.078895,0.276780,-0.739281}, - {0.006801,0.310959,-0.735661}, - {-0.168842,0.102387,-0.920381}, - {-0.104072,0.177278,-0.952530}, - {-0.129704,0.211848,-0.836678}, - {-0.099875,0.310931,-0.799381}, - {0.007237,0.361687,-0.794439}, - {-0.077913,0.258753,-0.921640}, - {0.007957,0.282241,-0.931680}, - {-0.252222,-0.550401,-0.557810}, - {-0.267633,-0.603419,-0.655209}, - {-0.446838,-0.118517,-0.466159}, - {-0.459488,-0.093017,-0.311341}, - {-0.370645,-0.100108,-0.159454}, - {-0.371984,-0.091991,-0.011044}, - {-0.328945,-0.098269,0.088659}, - {-0.282452,-0.018862,0.311501}, - {-0.352403,-0.131341,0.144902}, - {-0.364126,-0.200299,0.202388}, - {-0.283965,-0.231869,0.023668}, - {-0.298943,-0.155218,0.369716}, - {-0.293787,-0.121856,0.419097}, - {-0.290163,-0.290797,0.107824}, - {-0.264165,-0.272849,0.036347}, - {-0.228567,-0.372573,0.290309}, - {-0.190431,-0.286997,0.421917}, - {-0.191039,-0.240973,0.507118}, - {-0.287272,-0.276431,-0.065444}, - {-0.295675,-0.280818,-0.174200}, - {-0.399537,-0.313131,-0.376167}, - {-0.392666,-0.488581,-0.427494}, - {-0.331669,-0.570185,-0.466054}, - {-0.282290,-0.618140,-0.589220}, - {-0.374238,-0.594882,-0.323298}, - {-0.381071,-0.629723,-0.350777}, - {-0.382112,-0.624060,-0.221577}, - {-0.272701,-0.566522,0.259157}, - {-0.256702,-0.663406,0.286079}, - {-0.280948,-0.428359,0.055790}, - {-0.184974,-0.508894,0.326265}, - {-0.279971,-0.526918,0.395319}, - {-0.282599,-0.663393,0.412411}, - {-0.188329,-0.475093,0.417954}, - {-0.263384,-0.663396,0.466604}, - {-0.209063,-0.663393,0.509344}, - {-0.002044,-0.319624,0.553078}, - {-0.001266,-0.371260,0.413296}, - {-0.219753,-0.339762,-0.040921}, - {-0.256986,-0.282511,-0.006349}, - {-0.271706,-0.260881,0.001764}, - {-0.091191,-0.419184,-0.045912}, - {-0.114944,-0.429752,-0.124739}, - {-0.113970,-0.382987,-0.188540}, - {-0.243012,-0.464942,-0.242850}, - {-0.314815,-0.505402,-0.324768}, - {0.002774,-0.437526,-0.262766}, - {-0.072625,-0.417748,-0.221440}, - {-0.160112,-0.476932,-0.293450}, - {0.003859,-0.453425,-0.443916}, - {-0.120363,-0.581567,-0.438689}, - {-0.091499,-0.584191,-0.294511}, - {-0.116469,-0.599861,-0.188308}, - {-0.208032,-0.513640,-0.134649}, - {-0.235749,-0.610017,-0.040939}, - {-0.344916,-0.622487,-0.085380}, - {-0.336401,-0.531864,-0.212298}, - {0.001961,-0.459550,-0.135547}, - {-0.058296,-0.430536,-0.043440}, - {0.001378,-0.449511,-0.037762}, - {-0.130135,-0.510222,0.079144}, - {0.000142,-0.477549,0.157064}, - {-0.114284,-0.453206,0.304397}, - {-0.000592,-0.443558,0.285401}, - {-0.056215,-0.663402,0.326073}, - {-0.026248,-0.568010,0.273318}, - {-0.049261,-0.531064,0.389854}, - {-0.127096,-0.663398,0.479316}, - {-0.058384,-0.663401,0.372891}, - {-0.303961,0.054199,0.625921}, - {-0.268594,0.193403,0.502766}, - {-0.277159,0.126123,0.443289}, - {-0.287605,-0.005722,0.531844}, - {-0.231396,-0.121289,0.587387}, - {-0.253475,-0.081797,0.756541}, - {-0.195164,-0.137969,0.728011}, - {-0.167673,-0.156573,0.609388}, - {-0.145917,-0.169029,0.697600}, - {-0.077776,-0.214247,0.622586}, - {-0.076873,-0.214971,0.696301}, - {-0.002341,-0.233135,0.622859}, - {-0.002730,-0.213526,0.691267}, - {-0.003136,-0.192628,0.762731}, - {-0.056136,-0.201222,0.763806}, - {-0.114589,-0.166192,0.770723}, - {-0.155145,-0.129632,0.791738}, - {-0.183611,-0.058705,0.847012}, - {-0.165562,0.001980,0.833386}, - {-0.220084,0.019914,0.768935}, - {-0.255730,0.090306,0.670782}, - {-0.255594,0.113833,0.663389}, - {-0.226380,0.212655,0.617740}, - {-0.003367,-0.195342,0.799680}, - {-0.029743,-0.210508,0.827180}, - {-0.003818,-0.194783,0.873636}, - {-0.004116,-0.157907,0.931268}, - {-0.031280,-0.184555,0.889476}, - {-0.059885,-0.184448,0.841330}, - {-0.135333,-0.164332,0.878200}, - {-0.085574,-0.170948,0.925547}, - {-0.163833,-0.094170,0.897114}, - {-0.138444,-0.104250,0.945975}, - {-0.083497,-0.084934,0.979607}, - {-0.004433,-0.146642,0.985872}, - {-0.150715,0.032650,0.884111}, - {-0.135892,-0.035520,0.945455}, - {-0.070612,0.036849,0.975733}, - {-0.004458,-0.042526,1.015670}, - {-0.004249,0.046042,1.003240}, - {-0.086969,0.133224,0.947633}, - {-0.003873,0.161605,0.970499}, - {-0.125544,0.140012,0.917678}, - {-0.125651,0.250246,0.857602}, - {-0.003127,0.284070,0.878870}, - {-0.159174,0.125726,0.888878}, - {-0.183807,0.196970,0.844480}, - {-0.159890,0.291736,0.732480}, - {-0.199495,0.207230,0.779864}, - {-0.206182,0.164608,0.693257}, - {-0.186315,0.160689,0.817193}, - {-0.192827,0.166706,0.782271}, - {-0.175112,0.110008,0.860621}, - {-0.161022,0.057420,0.855111}, - {-0.172319,0.036155,0.816189}, - {-0.190318,0.064083,0.760605}, - {-0.195072,0.129179,0.731104}, - {-0.203126,0.410287,0.680536}, - {-0.216677,0.309274,0.642272}, - {-0.241515,0.311485,0.587832}, - {-0.002209,0.366663,0.749413}, - {-0.088230,0.396265,0.678635}, - {-0.170147,0.109517,0.840784}, - {-0.160521,0.067766,0.830650}, - {-0.181546,0.139805,0.812146}, - {-0.180495,0.148568,0.776087}, - {-0.180255,0.129125,0.744192}, - {-0.186298,0.078308,0.769352}, - {-0.167622,0.060539,0.806675}, - {-0.189876,0.102760,0.802582}, - {-0.108340,0.455446,0.657174}, - {-0.241585,0.527592,0.669296}, - {-0.265676,0.513366,0.634594}, - {-0.203073,0.478550,0.581526}, - {-0.266772,0.642330,0.602061}, - {-0.216961,0.564846,0.535435}, - {-0.202210,0.525495,0.475944}, - {-0.193888,0.467925,0.520606}, - {-0.265837,0.757267,0.500933}, - {-0.240306,0.653440,0.463215}, - {-0.309239,0.776868,0.304726}, - {-0.271009,0.683094,0.382018}, - {-0.312111,0.671099,0.286687}, - {-0.268791,0.624342,0.377231}, - {-0.302457,0.533996,0.360289}, - {-0.263656,0.529310,0.412564}, - {-0.282311,0.415167,0.447666}, - {-0.239201,0.442096,0.495604}, - {-0.220043,0.569026,0.445877}, - {-0.001263,0.395631,0.602029}, - {-0.057345,0.442535,0.572224}, - {-0.088927,0.506333,0.529106}, - {-0.125738,0.535076,0.612913}, - {-0.126251,0.577170,0.483159}, - {-0.149594,0.611520,0.557731}, - {-0.163188,0.660791,0.491080}, - {-0.172482,0.663387,0.415416}, - {-0.160464,0.591710,0.370659}, - {-0.156445,0.536396,0.378302}, - {-0.136496,0.444358,0.425226}, - {-0.095564,0.373768,0.473659}, - {-0.104146,0.315912,0.498104}, - {-0.000496,0.384194,0.473817}, - {-0.000183,0.297770,0.401486}, - {-0.129042,0.270145,0.434495}, - {0.000100,0.272963,0.349138}, - {-0.113060,0.236984,0.385554}, - {0.007260,0.016311,-0.883396}, - {0.007865,0.122104,-0.956137}, - {-0.032842,0.115282,-0.953252}, - {-0.089115,0.108449,-0.950317}, - {-0.047440,0.014729,-0.882756}, - {-0.104458,0.013137,-0.882070}, - {-0.086439,-0.584866,-0.608343}, - {-0.115026,-0.662605,-0.436732}, - {-0.071683,-0.665372,-0.606385}, - {-0.257884,-0.665381,-0.658052}, - {-0.272542,-0.665381,-0.592063}, - {-0.371322,-0.665382,-0.353620}, - {-0.372362,-0.665381,-0.224420}, - {-0.335166,-0.665380,-0.078623}, - {-0.225999,-0.665375,-0.038981}, - {-0.106719,-0.665374,-0.186351}, - {-0.081749,-0.665372,-0.292554}, - {0.006943,-0.091505,-0.858354}, - {0.006117,-0.280985,-0.769967}, - {0.004495,-0.502360,-0.559799}, - {-0.198638,-0.302135,-0.845816}, - {-0.237395,-0.542544,-0.587188}, - {-0.270001,-0.279489,-0.669861}, - {-0.134547,-0.119852,-0.959004}, - {-0.052088,-0.122463,-0.944549}, - {-0.124463,-0.293508,-0.899566}, - {-0.047616,-0.289643,-0.879292}, - {-0.168595,-0.529132,-0.654931}, - {-0.099793,-0.515719,-0.645873}, - {-0.186168,-0.605282,-0.724690}, - {-0.112970,-0.583097,-0.707469}, - {-0.108152,-0.665375,-0.700408}, - {-0.183019,-0.665378,-0.717630}, - {-0.349529,-0.334459,-0.511985}, - {-0.141182,-0.437705,-0.798194}, - {-0.212670,-0.448725,-0.737447}, - {-0.261111,-0.414945,-0.613835}, - {-0.077364,-0.431480,-0.778113}, - {0.005174,-0.425277,-0.651592}, - {0.089236,-0.431732,-0.777093}, - {0.271006,-0.415749,-0.610577}, - {0.223981,-0.449384,-0.734774}, - {0.153275,-0.438150,-0.796391}, - {0.358414,-0.335529,-0.507649}, - {0.193434,-0.665946,-0.715325}, - {0.118363,-0.665717,-0.699021}, - {0.123515,-0.583454,-0.706020}, - {0.196851,-0.605860,-0.722345}, - {0.109788,-0.516035,-0.644590}, - {0.178656,-0.529656,-0.652804}, - {0.061157,-0.289807,-0.878626}, - {0.138234,-0.293905,-0.897958}, - {0.066933,-0.122643,-0.943820}, - {0.149571,-0.120281,-0.957264}, - {0.280989,-0.280321,-0.666487}, - {0.246581,-0.543275,-0.584224}, - {0.211720,-0.302754,-0.843303}, - {0.086966,-0.665627,-0.291520}, - {0.110634,-0.665702,-0.185021}, - {0.228099,-0.666061,-0.036201}, - {0.337743,-0.666396,-0.074503}, - {0.376722,-0.666513,-0.219833}, - {0.377265,-0.666513,-0.349036}, - {0.281411,-0.666217,-0.588670}, - {0.267564,-0.666174,-0.654834}, - {0.080745,-0.665602,-0.605452}, - {0.122016,-0.662963,-0.435280}, - {0.095767,-0.585141,-0.607228}, - {0.118944,0.012799,-0.880702}, - {0.061944,0.014564,-0.882086}, - {0.104725,0.108156,-0.949130}, - {0.048513,0.115159,-0.952753}, - {0.112696,0.236643,0.386937}, - {0.128177,0.269757,0.436071}, - {0.102643,0.315600,0.499370}, - {0.094535,0.373481,0.474824}, - {0.136270,0.443946,0.426895}, - {0.157071,0.535923,0.380222}, - {0.161350,0.591224,0.372630}, - {0.173035,0.662865,0.417531}, - {0.162808,0.660299,0.493077}, - {0.148250,0.611070,0.559555}, - {0.125719,0.576790,0.484702}, - {0.123489,0.534699,0.614440}, - {0.087621,0.506066,0.530188}, - {0.055321,0.442365,0.572915}, - {0.219936,0.568361,0.448571}, - {0.238099,0.441375,0.498528}, - {0.281711,0.414315,0.451121}, - {0.263833,0.528513,0.415794}, - {0.303284,0.533081,0.363998}, - {0.269687,0.623528,0.380528}, - {0.314255,0.670153,0.290524}, - {0.272023,0.682273,0.385343}, - {0.311480,0.775931,0.308527}, - {0.240239,0.652714,0.466159}, - {0.265619,0.756464,0.504187}, - {0.192562,0.467341,0.522972}, - {0.201605,0.524885,0.478417}, - {0.215743,0.564193,0.538084}, - {0.264969,0.641527,0.605317}, - {0.201031,0.477940,0.584002}, - {0.263086,0.512567,0.637832}, - {0.238615,0.526867,0.672237}, - {0.105309,0.455123,0.658482}, - {0.183993,0.102195,0.804872}, - {0.161563,0.060042,0.808692}, - {0.180748,0.077754,0.771600}, - {0.175168,0.128588,0.746368}, - {0.175075,0.148030,0.778264}, - {0.175658,0.139265,0.814333}, - {0.154191,0.067291,0.832578}, - {0.163818,0.109013,0.842830}, - {0.084760,0.396004,0.679695}, - {0.238888,0.310760,0.590775}, - {0.213380,0.308625,0.644905}, - {0.199666,0.409678,0.683003}, - {0.190143,0.128597,0.733463}, - {0.184833,0.063516,0.762902}, - {0.166070,0.035644,0.818261}, - {0.154361,0.056943,0.857042}, - {0.168542,0.109489,0.862725}, - {0.187387,0.166131,0.784599}, - {0.180428,0.160135,0.819438}, - {0.201823,0.163991,0.695756}, - {0.194206,0.206635,0.782275}, - {0.155438,0.291260,0.734412}, - {0.177696,0.196424,0.846693}, - {0.152305,0.125256,0.890786}, - {0.119546,0.249876,0.859104}, - {0.118369,0.139643,0.919173}, - {0.079410,0.132973,0.948652}, - {0.062419,0.036648,0.976547}, - {0.127847,-0.035919,0.947070}, - {0.143624,0.032206,0.885913}, - {0.074888,-0.085173,0.980577}, - {0.130184,-0.104656,0.947620}, - {0.156201,-0.094653,0.899074}, - {0.077366,-0.171194,0.926545}, - {0.127722,-0.164729,0.879810}, - {0.052670,-0.184618,0.842019}, - {0.023477,-0.184638,0.889811}, - {0.022626,-0.210587,0.827500}, - {0.223089,0.211976,0.620493}, - {0.251444,0.113067,0.666494}, - {0.251419,0.089540,0.673887}, - {0.214360,0.019258,0.771595}, - {0.158999,0.001490,0.835374}, - {0.176696,-0.059249,0.849218}, - {0.148696,-0.130091,0.793599}, - {0.108290,-0.166528,0.772088}, - {0.049820,-0.201382,0.764454}, - {0.071341,-0.215195,0.697209}, - {0.073148,-0.214475,0.623510}, - {0.140502,-0.169461,0.699354}, - {0.163374,-0.157073,0.611416}, - {0.189466,-0.138550,0.730366}, - {0.247593,-0.082554,0.759610}, - {0.227468,-0.121982,0.590197}, - {0.284702,-0.006586,0.535347}, - {0.275741,0.125287,0.446676}, - {0.266650,0.192594,0.506044}, - {0.300086,0.053287,0.629620}, - {0.055450,-0.663935,0.375065}, - {0.122854,-0.664138,0.482323}, - {0.046520,-0.531571,0.391918}, - {0.024824,-0.568450,0.275106}, - {0.053855,-0.663931,0.328224}, - {0.112829,-0.453549,0.305788}, - {0.131265,-0.510617,0.080746}, - {0.061174,-0.430716,-0.042710}, - {0.341019,-0.532887,-0.208150}, - {0.347705,-0.623533,-0.081139}, - {0.238040,-0.610732,-0.038037}, - {0.211764,-0.514274,-0.132078}, - {0.120605,-0.600219,-0.186856}, - {0.096985,-0.584476,-0.293357}, - {0.127621,-0.581941,-0.437170}, - {0.165902,-0.477425,-0.291453}, - {0.077720,-0.417975,-0.220519}, - {0.320892,-0.506363,-0.320874}, - {0.248214,-0.465684,-0.239842}, - {0.118764,-0.383338,-0.187114}, - {0.118816,-0.430106,-0.123307}, - {0.094131,-0.419464,-0.044777}, - {0.274526,-0.261706,0.005110}, - {0.259842,-0.283292,-0.003185}, - {0.222861,-0.340431,-0.038210}, - {0.204445,-0.664380,0.513353}, - {0.259286,-0.664547,0.471281}, - {0.185402,-0.476020,0.421718}, - {0.279163,-0.664604,0.417328}, - {0.277157,-0.528122,0.400208}, - {0.183069,-0.509812,0.329995}, - {0.282599,-0.429210,0.059242}, - {0.254816,-0.664541,0.290687}, - {0.271436,-0.567707,0.263966}, - {0.386561,-0.625221,-0.216870}, - {0.387086,-0.630883,-0.346073}, - {0.380021,-0.596021,-0.318679}, - {0.291269,-0.619007,-0.585707}, - {0.339280,-0.571198,-0.461946}, - {0.400045,-0.489778,-0.422640}, - {0.406817,-0.314349,-0.371230}, - {0.300588,-0.281718,-0.170549}, - {0.290866,-0.277304,-0.061905}, - {0.187735,-0.241545,0.509437}, - {0.188032,-0.287569,0.424234}, - {0.227520,-0.373262,0.293102}, - {0.266526,-0.273650,0.039597}, - {0.291592,-0.291676,0.111386}, - {0.291914,-0.122741,0.422683}, - {0.297574,-0.156119,0.373368}, - {0.286603,-0.232731,0.027162}, - {0.364663,-0.201399,0.206850}, - {0.353855,-0.132408,0.149228}, - {0.282208,-0.019715,0.314960}, - {0.331187,-0.099266,0.092701}, - {0.375463,-0.093120,-0.006467}, - {0.375917,-0.101236,-0.154882}, - {0.466635,-0.094416,-0.305669}, - {0.455805,-0.119881,-0.460632}, - {0.277465,-0.604242,-0.651871}, - {0.261022,-0.551176,-0.554667}, - {0.093627,0.258494,-0.920589}, - {0.114248,0.310608,-0.798070}, - {0.144232,0.211434,-0.835001}, - {0.119916,0.176940,-0.951159}, - {0.184061,0.101854,-0.918220}, - {0.092431,0.276521,-0.738231}, - {0.133504,0.218403,-0.758602}, - {0.194987,0.097655,-0.812476}, - {0.185542,0.011005,-0.879202}, - {0.230315,-0.127450,-0.884202}, - {0.260471,0.255056,-0.624378}, - {0.351567,-0.042194,-0.663976}, - {0.253742,0.323524,-0.433716}, - {0.411612,0.132299,-0.438264}, - {0.270513,0.356530,-0.289984}, - {0.422146,0.162819,-0.273130}, - {0.164724,0.237490,0.208912}, - {0.253806,0.092900,0.240640}, - {0.203608,0.284597,0.096223}, - {0.241006,0.343093,-0.171396}, - {0.356076,0.149288,-0.143443}, - {0.337656,0.131992,0.066374} -}; - -udword BunnyTriangles[BUNNY_NBFACES][3]={ - {126,134,133}, - {342,138,134}, - {133,134,138}, - {126,342,134}, - {312,316,317}, - {169,163,162}, - {312,317,319}, - {312,319,318}, - {169,162,164}, - {169,168,163}, - {312,314,315}, - {169,164,165}, - {169,167,168}, - {312,315,316}, - {312,313,314}, - {169,165,166}, - {169,166,167}, - {312,318,313}, - {308,304,305}, - {308,305,306}, - {179,181,188}, - {177,173,175}, - {177,175,176}, - {302,293,300}, - {322,294,304}, - {188,176,175}, - {188,175,179}, - {158,177,187}, - {305,293,302}, - {305,302,306}, - {322,304,308}, - {188,181,183}, - {158,173,177}, - {293,298,300}, - {304,294,296}, - {304,296,305}, - {185,176,188}, - {185,188,183}, - {187,177,176}, - {187,176,185}, - {305,296,298}, - {305,298,293}, - {436,432, 28}, - {436, 28, 23}, - {434,278,431}, - { 30,208,209}, - { 30,209, 29}, - { 19, 20, 24}, - {208,207,211}, - {208,211,209}, - { 19,210,212}, - {433,434,431}, - {433,431,432}, - {433,432,436}, - {436,437,433}, - {277,275,276}, - {277,276,278}, - {209,210, 25}, - { 21, 26, 24}, - { 21, 24, 20}, - { 25, 26, 27}, - { 25, 27, 29}, - {435,439,277}, - {439,275,277}, - {432,431, 30}, - {432, 30, 28}, - {433,437,438}, - {433,438,435}, - {434,277,278}, - { 24, 25,210}, - { 24, 26, 25}, - { 29, 27, 28}, - { 29, 28, 30}, - { 19, 24,210}, - {208, 30,431}, - {208,431,278}, - {435,434,433}, - {435,277,434}, - { 25, 29,209}, - { 27, 22, 23}, - { 27, 23, 28}, - { 26, 22, 27}, - { 26, 21, 22}, - {212,210,209}, - {212,209,211}, - {207,208,278}, - {207,278,276}, - {439,435,438}, - { 12, 9, 10}, - { 12, 10, 13}, - { 2, 3, 5}, - { 2, 5, 4}, - { 16, 13, 14}, - { 16, 14, 17}, - { 22, 21, 16}, - { 13, 10, 11}, - { 13, 11, 14}, - { 1, 0, 3}, - { 1, 3, 2}, - { 15, 12, 16}, - { 19, 18, 15}, - { 19, 15, 16}, - { 19, 16, 20}, - { 9, 1, 2}, - { 9, 2, 10}, - { 3, 7, 8}, - { 3, 8, 5}, - { 16, 17, 23}, - { 16, 23, 22}, - { 21, 20, 16}, - { 10, 2, 4}, - { 10, 4, 11}, - { 0, 6, 7}, - { 0, 7, 3}, - { 12, 13, 16}, - {451,446,445}, - {451,445,450}, - {442,440,439}, - {442,439,438}, - {442,438,441}, - {421,420,422}, - {412,411,426}, - {412,426,425}, - {408,405,407}, - {413, 67, 68}, - {413, 68,414}, - {391,390,412}, - { 80,384,386}, - {404,406,378}, - {390,391,377}, - {390,377, 88}, - {400,415,375}, - {398,396,395}, - {398,395,371}, - {398,371,370}, - {112,359,358}, - {112,358,113}, - {351,352,369}, - {125,349,348}, - {345,343,342}, - {342,340,339}, - {341,335,337}, - {328,341,327}, - {331,323,333}, - {331,322,323}, - {327,318,319}, - {327,319,328}, - {315,314,324}, - {302,300,301}, - {302,301,303}, - {320,311,292}, - {285,284,289}, - {310,307,288}, - {310,288,290}, - {321,350,281}, - {321,281,282}, - {423,448,367}, - {272,273,384}, - {272,384,274}, - {264,265,382}, - {264,382,383}, - {440,442,261}, - {440,261,263}, - {252,253,254}, - {252,254,251}, - {262,256,249}, - {262,249,248}, - {228,243,242}, - {228, 31,243}, - {213,215,238}, - {213,238,237}, - { 19,212,230}, - {224,225,233}, - {224,233,231}, - {217,218, 56}, - {217, 56, 54}, - {217,216,239}, - {217,239,238}, - {217,238,215}, - {218,217,215}, - {218,215,214}, - { 6,102,206}, - {186,199,200}, - {197,182,180}, - {170,171,157}, - {201,200,189}, - {170,190,191}, - {170,191,192}, - {175,174,178}, - {175,178,179}, - {168,167,155}, - {122,149,158}, - {122,158,159}, - {135,153,154}, - {135,154,118}, - {143,140,141}, - {143,141,144}, - {132,133,136}, - {130,126,133}, - {124,125,127}, - {122,101,100}, - {122,100,121}, - {110,108,107}, - {110,107,109}, - { 98, 99, 97}, - { 98, 97, 64}, - { 98, 64, 66}, - { 87, 55, 57}, - { 83, 82, 79}, - { 83, 79, 84}, - { 78, 74, 50}, - { 49, 71, 41}, - { 49, 41, 37}, - { 49, 37, 36}, - { 58, 44, 60}, - { 60, 59, 58}, - { 51, 34, 33}, - { 39, 40, 42}, - { 39, 42, 38}, - {243,240, 33}, - {243, 33,229}, - { 39, 38, 6}, - { 44, 46, 40}, - { 55, 56, 57}, - { 64, 62, 65}, - { 64, 65, 66}, - { 41, 71, 45}, - { 75, 50, 51}, - { 81, 79, 82}, - { 77, 88, 73}, - { 93, 92, 94}, - { 68, 47, 46}, - { 96, 97, 99}, - { 96, 99, 95}, - {110,109,111}, - {111,112,110}, - {114,113,123}, - {114,123,124}, - {132,131,129}, - {133,137,136}, - {135,142,145}, - {145,152,135}, - {149,147,157}, - {157,158,149}, - {164,150,151}, - {153,163,168}, - {153,168,154}, - {185,183,182}, - {185,182,184}, - {161,189,190}, - {200,199,191}, - {200,191,190}, - {180,178,195}, - {180,195,196}, - {102,101,204}, - {102,204,206}, - { 43, 48,104}, - { 43,104,103}, - {216,217, 54}, - {216, 54, 32}, - {207,224,231}, - {230,212,211}, - {230,211,231}, - {227,232,241}, - {227,241,242}, - {235,234,241}, - {235,241,244}, - {430,248,247}, - {272,274,253}, - {272,253,252}, - {439,260,275}, - {225,224,259}, - {225,259,257}, - {269,270,407}, - {269,407,405}, - {270,269,273}, - {270,273,272}, - {273,269,268}, - {273,268,267}, - {273,267,266}, - {273,266,265}, - {273,265,264}, - {448,279,367}, - {281,350,368}, - {285,286,301}, - {290,323,310}, - {290,311,323}, - {282,281,189}, - {292,311,290}, - {292,290,291}, - {307,306,302}, - {307,302,303}, - {316,315,324}, - {316,324,329}, - {331,351,350}, - {330,334,335}, - {330,335,328}, - {341,337,338}, - {344,355,354}, - {346,345,348}, - {346,348,347}, - {364,369,352}, - {364,352,353}, - {365,363,361}, - {365,361,362}, - {376,401,402}, - {373,372,397}, - {373,397,400}, - {376, 92,377}, - {381,378,387}, - {381,387,385}, - {386, 77, 80}, - {390,389,412}, - {416,417,401}, - {403,417,415}, - {408,429,430}, - {419,423,418}, - {427,428,444}, - {427,444,446}, - {437,436,441}, - {450,445, 11}, - {450, 11, 4}, - {447,449, 5}, - {447, 5, 8}, - {441,438,437}, - {425,426,451}, - {425,451,452}, - {417,421,415}, - {408,407,429}, - {399,403,400}, - {399,400,397}, - {394,393,416}, - {389,411,412}, - {386,383,385}, - {408,387,378}, - {408,378,406}, - {377,391,376}, - { 94,375,415}, - {372,373,374}, - {372,374,370}, - {359,111,360}, - {359,112,111}, - {113,358,349}, - {113,349,123}, - {346,343,345}, - {343,340,342}, - {338,336,144}, - {338,144,141}, - {327,341,354}, - {327,354,326}, - {331,350,321}, - {331,321,322}, - {314,313,326}, - {314,326,325}, - {300,298,299}, - {300,299,301}, - {288,287,289}, - {189,292,282}, - {287,288,303}, - {284,285,297}, - {368,280,281}, - {448,447,279}, - {274,226,255}, - {267,268,404}, - {267,404,379}, - {429,262,430}, - {439,440,260}, - {257,258,249}, - {257,249,246}, - {430,262,248}, - {234,228,242}, - {234,242,241}, - {237,238,239}, - {237,239,236}, - { 15, 18,227}, - { 15,227,229}, - {222,223, 82}, - {222, 82, 83}, - {214,215,213}, - {214,213, 81}, - { 38,102, 6}, - {122,159,200}, - {122,200,201}, - {174,171,192}, - {174,192,194}, - {197,193,198}, - {190,170,161}, - {181,179,178}, - {181,178,180}, - {166,156,155}, - {163,153,152}, - {163,152,162}, - {120,156,149}, - {120,149,121}, - {152,153,135}, - {140,143,142}, - {135,131,132}, - {135,132,136}, - {130,129,128}, - {130,128,127}, - {100,105,119}, - {100,119,120}, - {106,104,107}, - {106,107,108}, - { 91, 95, 59}, - { 93, 94, 68}, - { 91, 89, 92}, - { 76, 53, 55}, - { 76, 55, 87}, - { 81, 78, 79}, - { 74, 73, 49}, - { 69, 60, 45}, - { 58, 62, 64}, - { 58, 64, 61}, - { 53, 31, 32}, - { 32, 54, 53}, - { 42, 43, 38}, - { 35, 36, 0}, - { 35, 0, 1}, - { 34, 35, 1}, - { 34, 1, 9}, - { 44, 40, 41}, - { 44, 41, 45}, - { 33,240, 51}, - { 63, 62, 58}, - { 63, 58, 59}, - { 45, 71, 70}, - { 76, 75, 51}, - { 76, 51, 52}, - { 86, 85, 84}, - { 86, 84, 87}, - { 89, 72, 73}, - { 89, 73, 88}, - { 91, 92, 96}, - { 91, 96, 95}, - { 72, 91, 60}, - { 72, 60, 69}, - {104,106,105}, - {119,105,117}, - {119,117,118}, - {124,127,128}, - {117,116,129}, - {117,129,131}, - {118,117,131}, - {135,140,142}, - {146,150,152}, - {146,152,145}, - {149,122,121}, - {166,165,151}, - {166,151,156}, - {158,172,173}, - {161,160,189}, - {199,198,193}, - {199,193,191}, - {204,201,202}, - {178,174,194}, - {200,159,186}, - {109, 48, 67}, - { 48,107,104}, - {216, 32,236}, - {216,236,239}, - {223,214, 81}, - {223, 81, 82}, - { 33, 12, 15}, - { 32,228,234}, - { 32,234,236}, - {240, 31, 52}, - {256,255,246}, - {256,246,249}, - {258,263,248}, - {258,248,249}, - {275,260,259}, - {275,259,276}, - {207,276,259}, - {270,271,429}, - {270,429,407}, - {413,418,366}, - {413,366,365}, - {368,367,279}, - {368,279,280}, - {303,301,286}, - {303,286,287}, - {283,282,292}, - {283,292,291}, - {320,292,189}, - {298,296,297}, - {298,297,299}, - {318,327,326}, - {318,326,313}, - {329,330,317}, - {336,333,320}, - {326,354,353}, - {334,332,333}, - {334,333,336}, - {342,339,139}, - {342,139,138}, - {345,342,126}, - {347,357,356}, - {369,368,351}, - {363,356,357}, - {363,357,361}, - {366,367,368}, - {366,368,369}, - {375,373,400}, - { 92, 90,377}, - {409,387,408}, - {386,385,387}, - {386,387,388}, - {412,394,391}, - {396,398,399}, - {408,406,405}, - {415,421,419}, - {415,419,414}, - {425,452,448}, - {425,448,424}, - {444,441,443}, - {448,452,449}, - {448,449,447}, - {446,444,443}, - {446,443,445}, - {250,247,261}, - {250,261,428}, - {421,422,423}, - {421,423,419}, - {427,410,250}, - {417,403,401}, - {403,402,401}, - {420,392,412}, - {420,412,425}, - {420,425,424}, - {386,411,389}, - {383,382,381}, - {383,381,385}, - {378,379,404}, - {372,371,395}, - {372,395,397}, - {371,372,370}, - {361,359,360}, - {361,360,362}, - {368,350,351}, - {349,347,348}, - {356,355,344}, - {356,344,346}, - {344,341,340}, - {344,340,343}, - {338,337,336}, - {328,335,341}, - {324,352,351}, - {324,351,331}, - {320,144,336}, - {314,325,324}, - {322,308,309}, - {310,309,307}, - {287,286,289}, - {203,280,279}, - {203,279,205}, - {297,295,283}, - {297,283,284}, - {447,205,279}, - {274,384, 80}, - {274, 80,226}, - {266,267,379}, - {266,379,380}, - {225,257,246}, - {225,246,245}, - {256,254,253}, - {256,253,255}, - {430,247,250}, - {226,235,244}, - {226,244,245}, - {232,233,244}, - {232,244,241}, - {230, 18, 19}, - { 32, 31,228}, - {219,220, 86}, - {219, 86, 57}, - {226,213,235}, - {206, 7, 6}, - {122,201,101}, - {201,204,101}, - {180,196,197}, - {170,192,171}, - {200,190,189}, - {194,193,195}, - {183,181,180}, - {183,180,182}, - {155,154,168}, - {149,156,151}, - {149,151,148}, - {155,156,120}, - {145,142,143}, - {145,143,146}, - {136,137,140}, - {133,132,130}, - {128,129,116}, - {100,120,121}, - {110,112,113}, - {110,113,114}, - { 66, 65, 63}, - { 66, 63, 99}, - { 66, 99, 98}, - { 96, 46, 61}, - { 89, 88, 90}, - { 86, 87, 57}, - { 80, 78, 81}, - { 72, 69, 49}, - { 67, 48, 47}, - { 67, 47, 68}, - { 56, 55, 53}, - { 50, 49, 36}, - { 50, 36, 35}, - { 40, 39, 41}, - {242,243,229}, - {242,229,227}, - { 6, 37, 39}, - { 42, 47, 48}, - { 42, 48, 43}, - { 61, 46, 44}, - { 45, 70, 69}, - { 69, 70, 71}, - { 69, 71, 49}, - { 74, 78, 77}, - { 83, 84, 85}, - { 73, 74, 77}, - { 93, 96, 92}, - { 68, 46, 93}, - { 95, 99, 63}, - { 95, 63, 59}, - {115,108,110}, - {115,110,114}, - {125,126,127}, - {129,130,132}, - {137,133,138}, - {137,138,139}, - {148,146,143}, - {148,143,147}, - {119,118,154}, - {161,147,143}, - {165,164,151}, - {158,157,171}, - {158,171,172}, - {159,158,187}, - {159,187,186}, - {194,192,191}, - {194,191,193}, - {189,202,201}, - {182,197,184}, - {205, 8, 7}, - { 48,109,107}, - {218,219, 57}, - {218, 57, 56}, - {207,231,211}, - {232,230,231}, - {232,231,233}, - { 53, 52, 31}, - {388,411,386}, - {409,430,250}, - {262,429,254}, - {262,254,256}, - {442,444,428}, - {273,264,383}, - {273,383,384}, - {429,271,251}, - {429,251,254}, - {413,365,362}, - { 67,413,360}, - {282,283,295}, - {285,301,299}, - {202,281,280}, - {284,283,291}, - {284,291,289}, - {320,189,160}, - {308,306,307}, - {307,309,308}, - {319,317,330}, - {319,330,328}, - {353,352,324}, - {332,331,333}, - {340,341,338}, - {354,341,344}, - {349,358,357}, - {349,357,347}, - {364,355,356}, - {364,356,363}, - {364,365,366}, - {364,366,369}, - {374,376,402}, - {375, 92,373}, - { 77,389,390}, - {382,380,381}, - {389, 77,386}, - {393,394,412}, - {393,412,392}, - {401,394,416}, - {415,400,403}, - {411,410,427}, - {411,427,426}, - {422,420,424}, - {247,248,263}, - {247,263,261}, - {445,443, 14}, - {445, 14, 11}, - {449,450, 4}, - {449, 4, 5}, - {443,441, 17}, - {443, 17, 14}, - {436, 23, 17}, - {436, 17,441}, - {424,448,422}, - {448,423,422}, - {414,419,418}, - {414,418,413}, - {406,404,405}, - {399,397,395}, - {399,395,396}, - {420,416,392}, - {388,410,411}, - {386,384,383}, - {390, 88, 77}, - {375, 94, 92}, - {415,414, 68}, - {415, 68, 94}, - {370,374,402}, - {370,402,398}, - {361,357,358}, - {361,358,359}, - {125,348,126}, - {346,344,343}, - {340,338,339}, - {337,335,334}, - {337,334,336}, - {325,353,324}, - {324,331,332}, - {324,332,329}, - {323,322,309}, - {323,309,310}, - {294,295,297}, - {294,297,296}, - {289,286,285}, - {202,280,203}, - {288,307,303}, - {282,295,321}, - { 67,360,111}, - {418,423,367}, - {418,367,366}, - {272,252,251}, - {272,251,271}, - {272,271,270}, - {255,253,274}, - {265,266,380}, - {265,380,382}, - {442,428,261}, - {440,263,258}, - {440,258,260}, - {409,250,410}, - {255,226,245}, - {255,245,246}, - { 31,240,243}, - {236,234,235}, - {236,235,237}, - {233,225,245}, - {233,245,244}, - {220,221, 85}, - {220, 85, 86}, - { 81,213,226}, - { 81,226, 80}, - { 7,206,205}, - {186,184,198}, - {186,198,199}, - {204,203,205}, - {204,205,206}, - {195,193,196}, - {171,174,172}, - {173,174,175}, - {173,172,174}, - {155,167,166}, - {160,161,143}, - {160,143,144}, - {119,154,155}, - {148,151,150}, - {148,150,146}, - {140,137,139}, - {140,139,141}, - {127,126,130}, - {114,124,128}, - {114,128,115}, - {117,105,106}, - {117,106,116}, - {104,105,100}, - {104,100,103}, - { 59, 60, 91}, - { 97, 96, 61}, - { 97, 61, 64}, - { 91, 72, 89}, - { 87, 84, 79}, - { 87, 79, 76}, - { 78, 80, 77}, - { 49, 50, 74}, - { 60, 44, 45}, - { 61, 44, 58}, - { 51, 50, 35}, - { 51, 35, 34}, - { 39, 37, 41}, - { 33, 34, 9}, - { 33, 9, 12}, - { 0, 36, 37}, - { 0, 37, 6}, - { 40, 46, 47}, - { 40, 47, 42}, - { 53, 54, 56}, - { 65, 62, 63}, - { 72, 49, 73}, - { 79, 78, 75}, - { 79, 75, 76}, - { 52, 53, 76}, - { 92, 89, 90}, - { 96, 93, 46}, - {102,103,100}, - {102,100,101}, - {116,106,108}, - {116,108,115}, - {123,125,124}, - {116,115,128}, - {118,131,135}, - {140,135,136}, - {148,147,149}, - {120,119,155}, - {164,162,152}, - {164,152,150}, - {157,147,161}, - {157,161,170}, - {186,187,185}, - {186,185,184}, - {193,197,196}, - {202,203,204}, - {194,195,178}, - {198,184,197}, - { 67,111,109}, - { 38, 43,103}, - { 38,103,102}, - {214,223,222}, - {214,222,221}, - {214,221,220}, - {214,220,219}, - {214,219,218}, - {213,237,235}, - {221,222, 83}, - {221, 83, 85}, - { 15,229, 33}, - {227, 18,230}, - {227,230,232}, - { 52, 51,240}, - { 75, 78, 50}, - {408,430,409}, - {260,258,257}, - {260,257,259}, - {224,207,259}, - {268,269,405}, - {268,405,404}, - {413,362,360}, - {447, 8,205}, - {299,297,285}, - {189,281,202}, - {290,288,289}, - {290,289,291}, - {322,321,295}, - {322,295,294}, - {333,323,311}, - {333,311,320}, - {317,316,329}, - {320,160,144}, - {353,325,326}, - {329,332,334}, - {329,334,330}, - {339,338,141}, - {339,141,139}, - {348,345,126}, - {347,356,346}, - {123,349,125}, - {364,353,354}, - {364,354,355}, - {365,364,363}, - {376,391,394}, - {376,394,401}, - { 92,376,374}, - { 92,374,373}, - {377, 90, 88}, - {380,379,378}, - {380,378,381}, - {388,387,409}, - {388,409,410}, - {416,393,392}, - {399,398,402}, - {399,402,403}, - {250,428,427}, - {421,417,416}, - {421,416,420}, - {426,427,446}, - {426,446,451}, - {444,442,441}, - {452,451,450}, - {452,450,449} -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Constructor. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Bunny::Bunny() -{ - mNbVerts = BUNNY_NBVERTICES; - mNbFaces = BUNNY_NBFACES; - mVerts = (Point*)BunnyVertices; - mFaces = (udword*)BunnyTriangles; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Destructor. - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Bunny::~Bunny() -{ -} diff --git a/TestOpcode/IceBunny.h b/TestOpcode/IceBunny.h deleted file mode 100644 index 6b1f730..0000000 --- a/TestOpcode/IceBunny.h +++ /dev/null @@ -1,34 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains a bunny object. - * \file IceBunny.h - * \author Pierre Terdiman - * \date January, 17, 1999 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEBUNNY_H__ -#define __ICEBUNNY_H__ - - class Bunny - { - public: - // Constructor / Destructor - Bunny(); - ~Bunny(); - // Data access - __forceinline udword GetNbVerts() const { return mNbVerts; } - __forceinline udword GetNbFaces() const { return mNbFaces; } - __forceinline Point* GetVerts() const { return mVerts; } - __forceinline udword* GetFaces() const { return mFaces; } - - protected: - udword mNbVerts; //!< Number of vertices - udword mNbFaces; //!< Number of faces - Point* mVerts; //!< List of vertices - udword* mFaces; //!< List of faces - }; - -#endif // __ICEBUNNY_H__ diff --git a/TestOpcode/ReadMe.txt b/TestOpcode/ReadMe.txt deleted file mode 100644 index 0eb89e5..0000000 --- a/TestOpcode/ReadMe.txt +++ /dev/null @@ -1,34 +0,0 @@ -======================================================================== - CONSOLE APPLICATION : TestOpcode -======================================================================== - - -AppWizard has created this TestOpcode application for you. - -This file contains a summary of what you will find in each of the files that -make up your TestOpcode application. - -TestOpcode.dsp - This file (the project file) contains information at the project level and - is used to build a single project or subproject. Other users can share the - project (.dsp) file, but they should export the makefiles locally. - -TestOpcode.cpp - This is the main application source file. - - -///////////////////////////////////////////////////////////////////////////// -Other standard files: - -StdAfx.h, StdAfx.cpp - These files are used to build a precompiled header (PCH) file - named TestOpcode.pch and a precompiled types file named StdAfx.obj. - - -///////////////////////////////////////////////////////////////////////////// -Other notes: - -AppWizard uses "TODO:" to indicate parts of the source code you -should add to or customize. - -///////////////////////////////////////////////////////////////////////////// diff --git a/TestOpcode/StdAfx.cpp b/TestOpcode/StdAfx.cpp deleted file mode 100644 index a27b824..0000000 --- a/TestOpcode/StdAfx.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "stdafx.h" diff --git a/TestOpcode/StdAfx.h b/TestOpcode/StdAfx.h deleted file mode 100644 index d498e05..0000000 --- a/TestOpcode/StdAfx.h +++ /dev/null @@ -1,23 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#if !defined(AFX_STDAFX_H__D63E9666_3FC9_11D5_8B0F_0050BAC83302__INCLUDED_) -#define AFX_STDAFX_H__D63E9666_3FC9_11D5_8B0F_0050BAC83302__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#include "Opcode.h" -using namespace Opcode; - -#include "IceBunny.h" - -// TODO: reference additional headers your program requires here - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__D63E9666_3FC9_11D5_8B0F_0050BAC83302__INCLUDED_) diff --git a/TestOpcode/TestOpcode.cpp b/TestOpcode/TestOpcode.cpp deleted file mode 100644 index 12a343d..0000000 --- a/TestOpcode/TestOpcode.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "stdafx.h" - -static Bunny gBunny; - -int main(int argc, char* argv[]) -{ - // 1) Build an OPCODE_Model using a creation structure: - OPCODE_Model Sample; - - // 1-1) Build a quantized no-leaf tree - OPCODECREATE OPCC; - OPCC.NbTris = gBunny.GetNbFaces(); - OPCC.NbVerts = gBunny.GetNbVerts(); - OPCC.Tris = gBunny.GetFaces(); - OPCC.Verts = gBunny.GetVerts(); -// OPCC.Rules = SPLIT_COMPLETE | SPLIT_LARGESTAXIS; - OPCC.Rules = SPLIT_COMPLETE | SPLIT_SPLATTERPOINTS; - OPCC.NoLeaf = true; - OPCC.Quantized = true; - Sample.Build(OPCC); - - // 2) Create a tree collider and setup it: - AABBTreeCollider TC; - TC.SetFirstContact(false); // report all contacts - TC.SetFullBoxBoxTest(false); // use coarse BV-BV tests - TC.SetFullPrimBoxTest(false); // use coarse primitive-BV tests - TC.SetTemporalCoherence(false); // don't use temporal coherence - - // 3) Setup object callbacks. Geometry & topology are NOT stored in the collision system, - // in order to save some ram. So, when the system needs them to perform accurate intersection - // tests, you're requested to provide the triangle-vertices corresponding to a given face index. - struct Local - { - static void ColCallback(udword triangleindex, VertexPointers& triangle, udword userdata) - { - // Get correct triangle in the app-controlled database - udword* Tri = &gBunny.GetFaces()[triangleindex*3]; - // Setup pointers to vertices for the collision system - triangle.Vertex[0] = &gBunny.GetVerts()[Tri[0]]; - triangle.Vertex[1] = &gBunny.GetVerts()[Tri[1]]; - triangle.Vertex[2] = &gBunny.GetVerts()[Tri[2]]; - } - }; - - // 3-1) Setup callbacks - TC.SetCallbackObj0(Local::ColCallback); - TC.SetCallbackObj1(Local::ColCallback); - - // Of course, you should make this callback as fast as possible. And you're also not supposed - // to modify the geometry *after* the collision trees have been built. The alternative was to - // store the geometry & topology in the collision system as well (as in RAPID) but we have found - // this approach to waste a lot of ram in many cases. - - // 4) Perform a collision query - - // 4-1) Setup cache - static BVTCache ColCache; - ColCache.Model0 = &Sample; - ColCache.Model1 = &Sample; - - // 4-2) Collision query - Matrix4x4 World0, World1; - World0.Identity(); - World1.Identity(); - World1.SetTrans(0.5f, 0.0f, 0.0f); // A little translation - bool IsOk = TC.Collide(ColCache, World0, World1); - - // 4-3) Get collision status => if true, objects overlap - bool Status = TC.GetContactStatus(); - if(Status) printf("Overlap detected!\n"); - - // 4-4) Number of colliding pairs and list of pairs - udword NbPairs = TC.GetNbPairs(); - Pair* p = TC.GetPairs(); // list of colliding triangles - - // 5) Stats - printf("%d bytes used in the collision tree.\n", Sample.GetUsedBytes()); - printf("%d BV-BV overlap tests.\n", TC.GetNbBVBVTests()); - printf("%d Triangle-Triangle overlap tests.\n", TC.GetNbPrimPrimTests()); - printf("%d Triangle-BV overlap tests.\n", TC.GetNbBVPrimTests()); - - return 0; -} diff --git a/TestOpcode/TestOpcode.dsp b/TestOpcode/TestOpcode.dsp deleted file mode 100644 index d00c13f..0000000 --- a/TestOpcode/TestOpcode.dsp +++ /dev/null @@ -1,123 +0,0 @@ -# Microsoft Developer Studio Project File - Name="TestOpcode" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=TestOpcode - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "TestOpcode.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "TestOpcode.mak" CFG="TestOpcode - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "TestOpcode - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "TestOpcode - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "TestOpcode - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /G6 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c -# ADD BASE RSC /l 0x40c /d "NDEBUG" -# ADD RSC /l 0x40c /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "TestOpcode - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /G6 /MDd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c -# ADD BASE RSC /l 0x40c /d "_DEBUG" -# ADD RSC /l 0x40c /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "TestOpcode - Win32 Release" -# Name "TestOpcode - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\IceBunny.cpp -# End Source File -# Begin Source File - -SOURCE=.\StdAfx.cpp -# ADD CPP /Yc"stdafx.h" -# End Source File -# Begin Source File - -SOURCE=.\TestOpcode.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\IceBunny.h -# End Source File -# Begin Source File - -SOURCE=.\StdAfx.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Source File - -SOURCE=.\ReadMe.txt -# End Source File -# End Target -# End Project diff --git a/TestOpcode/TestOpcode.dsw b/TestOpcode/TestOpcode.dsw deleted file mode 100644 index 417c71a..0000000 --- a/TestOpcode/TestOpcode.dsw +++ /dev/null @@ -1,44 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "OpcodeDistrib"=..\..\APIs\OpcodeDistrib\OpcodeDistrib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "TestOpcode"=.\TestOpcode.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name OpcodeDistrib - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### -