diff --git a/OpcodeDistrib/OPC_AABB.cpp b/OpcodeDistrib/IceAABB.cpp similarity index 80% rename from OpcodeDistrib/OPC_AABB.cpp rename to OpcodeDistrib/IceAABB.cpp index 8d55f30..294ee57 100644 --- a/OpcodeDistrib/OPC_AABB.cpp +++ b/OpcodeDistrib/IceAABB.cpp @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains AABB-related code. - * \file OPC_AABB.cpp + * \file IceAABB.cpp * \author Pierre Terdiman * \date January, 29, 2000 */ @@ -28,13 +20,9 @@ // Precompiled Header #include "Stdafx.h" -using namespace Opcode; - -#ifndef __MESHMERIZER_H__ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the sum of two AABBs. + * Computes the sum of two AABBs. * \param aabb [in] the other AABB * \return Self-Reference */ @@ -55,4 +43,22 @@ AABB& AABB::Add(const AABB& aabb) return *this; } -#endif // __MESHMERIZER_H__ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 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) 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; + } + // Data access //! Get min point of the box @@ -95,9 +119,18 @@ //! Get component of the box's extents along a given axis __forceinline float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; } + //! Get box diagonal + __forceinline void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; } + __forceinline float GetWidth() const { return mMax.x - mMin.x; } + __forceinline float GetHeight() const { return mMax.y - mMin.y; } + __forceinline float GetDepth() const { return mMax.z - mMin.z; } + + //! Volume + __forceinline float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the intersection between two AABBs. + * Computes the intersection between two AABBs. * \param a [in] the other AABB * \return true on intersection */ @@ -116,7 +149,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the 1D-intersection between two AABBs, on a given axis. + * Computes the 1D-intersection between two AABBs, on a given axis. * \param a [in] the other AABB * \param axis [in] the axis (0, 1, 2) * \return true on intersection @@ -130,7 +163,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to recompute the AABB after an arbitrary transform by a 4x4 matrix. + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it) * \param mtx [in] the transform matrix * \param aabb [out] the transformed AABB [can be *this] @@ -160,6 +193,48 @@ 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; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline 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. + __forceinline AABB& operator*=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents * s); + return *this; + } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + __forceinline AABB& operator/=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents / s); + return *this; + } + + //! Operator for AABB += Point. Translates the box. + __forceinline AABB& operator+=(const Point& trans) + { + mMin+=trans; + mMax+=trans; + return *this; + } + private: Point mMin; //!< Min point Point mMax; //!< Max point @@ -167,7 +242,7 @@ #else - class OPCODE_API AABB + class MESHMERIZER_API AABB { public: //! Constructor @@ -180,7 +255,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to setup an AABB from min & max vectors. + * Setups an AABB from min & max vectors. * \param min [in] the min point * \param max [in] the max point */ @@ -189,7 +264,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to setup an AABB from center & extents vectors. + * Setups an AABB from center & extents vectors. * \param c [in] the center point * \param e [in] the extents vector */ @@ -198,19 +273,49 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to setup an empty AABB. + * Setups an empty AABB. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to get the size of the AABB. The size is defined as the longest extent. + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. * \return the size of the AABB */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float GetSize() const { return mExtents.Max(); } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the nex point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + 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.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); + } + // Data access //! Get min point of the box @@ -233,9 +338,18 @@ //! Get component of the box's extents along a given axis __forceinline float GetExtents(udword axis) const { return mExtents[axis]; } + //! Get box diagonal + __forceinline void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; } + __forceinline float GetWidth() const { return mExtents.x * 2.0f; } + __forceinline float GetHeight() const { return mExtents.y * 2.0f; } + __forceinline float GetDepth() const { return mExtents.z * 2.0f; } + + //! Volume + __forceinline float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the intersection between two AABBs. + * Computes the intersection between two AABBs. * \param a [in] the other AABB * \return true on intersection */ @@ -265,7 +379,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the 1D-intersection between two AABBs, on a given axis. + * Computes the 1D-intersection between two AABBs, on a given axis. * \param a [in] the other AABB * \param axis [in] the axis (0, 1, 2) * \return true on intersection @@ -281,7 +395,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to recompute the AABB after an arbitrary transform by a 4x4 matrix. + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. * \param mtx [in] the transform matrix * \param aabb [out] the transformed AABB [can be *this] */ @@ -305,6 +419,35 @@ aabb.mExtents.y = Ex.y + Ey.y + Ez.y; aabb.mExtents.z = Ex.z + Ey.z + Ez.z; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline bool IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0 + if(mExtents.x < 0.0f) return false; + if(mExtents.y < 0.0f) return false; + if(mExtents.z < 0.0f) return false; + return true; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + __forceinline AABB& operator*=(float s) { mExtents*=s; return *this; } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + __forceinline AABB& operator/=(float s) { mExtents/=s; return *this; } + + //! Operator for AABB += Point. Translates the box. + __forceinline AABB& operator+=(const Point& trans) + { + mCenter+=trans; + return *this; + } + private: Point mCenter; //!< AABB Center Point mExtents; //!< x, y and z extents @@ -312,4 +455,30 @@ #endif + __forceinline void ComputeMinMax(const Point& p, Point& min, Point& max) + { + 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.z > max.z) max.z = p.z; + if(p.z < min.z) min.z = p.z; + } + + __forceinline void ComputeAABB(AABB& aabb, const Point* list, udword nbpts) + { + if(list) + { + Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); + for(udword i=0;i=0 && i=0 && i>31); + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). + __forceinline float frsqrt(float f) + { + float x = f * 0.5f; + udword y = 0x5f3759df - ((udword&)f >> 1); + // Iteration... + (float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) ); + // Result + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). Comes from NVIDIA. + __forceinline float InvSqrt(const float& x) + { + udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1; + float y = *(float*)&tmp; + return y * (1.47f - 0.47f * x * y * y); + } + + //! TO BE DOCUMENTED + __forceinline float fsqrt(float f) + { + udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000; + // Iteration...? + // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f; + // Result + return (float&)y; + } + + //! Returns the float ranged espilon value. + __forceinline float fepsilon(float f) + { + udword b = (udword&)f & 0xff800000; + udword a = b | 0x00000001; + (float&)a -= (float&)b; + // Result + return (float&)a; + } + + //! Is the float valid ? + __forceinline bool IsNAN(float value) { return ((*(udword*)&value)&0x7f800000)==0x7f800000; } + #define NaN(value) (!((value>=0) || (value<0))) + +/* + //! FPU precision setting function. + __forceinline void SetFPU() + { + // This function evaluates whether the floating-point + // control word is set to single precision/round to nearest/ + // exceptions disabled. If these conditions don't hold, the + // function changes the control word to set them and returns + // TRUE, putting the old control word value in the passback + // location pointed to by pwOldCW. + { + uword wTemp, wSave; + + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } + } + } +*/ + //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON) + __forceinline float ComputeFloatEpsilon() + { + float f = 1.0f; + ((udword&)f)^=1; + return f - 1.0f; // You can check it's the same as FLT_EPSILON + } + + __forceinline bool IsFloatZero(float x, float epsilon=1e-6f) + { + return x*x < epsilon; + } + + #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 + #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 + #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 + #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0 + + #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1 + #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1 + #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1 + #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1 + + #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2 + #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2 + #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 + #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 + + //! A global function to find MAX(a,b,c) using FCOMI/FCMOV + __forceinline float FCMax3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVB_ST1 + FCOMI_ST2 + FCMOVB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } + + //! A global function to find MIN(a,b,c) using FCOMI/FCMOV + __forceinline float FCMin3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVNB_ST1 + FCOMI_ST2 + FCMOVNB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } + + __forceinline int ConvertToSortable(float f) + { + int& Fi = (int&)f; + int Fmask = (Fi>>31); + Fi ^= Fmask; + Fmask &= ~(1<<31); + Fi -= Fmask; + return Fi; + } + + enum FPUMode + { + FPU_FLOOR = 0, + FPU_CEIL = 1, + FPU_BEST = 2, + FPU_FORCE_DWORD = 0x7fffffff + }; + + ICECORE_API FPUMode GetFPUMode(); + ICECORE_API void SaveFPU(); + ICECORE_API void RestoreFPU(); + ICECORE_API void SetFPUFloorMode(); + ICECORE_API void SetFPUCeilMode(); + ICECORE_API void SetFPUBestMode(); + +#endif // __ICEFPU_H__ diff --git a/OpcodeDistrib/IceHPoint.h b/OpcodeDistrib/IceHPoint.h new file mode 100644 index 0000000..3a53f37 --- /dev/null +++ b/OpcodeDistrib/IceHPoint.h @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous vectors. + * \file IceHPoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEHPOINT_H__ +#define __ICEHPOINT_H__ + + enum PointComponent + { + _X = 0, + _Y = 1, + _Z = 2, + _W = 3, + _FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API HPoint : public Point + { + public: + + //! Empty constructor + __forceinline HPoint() {} + //! Constructor from floats + __forceinline HPoint(float _x, float _y, float _z, float _w=0.0f) : Point(_x, _y, _z), w(_w) {} + //! Constructor from array + __forceinline HPoint(float f[4]) : Point(f), w(f[3]) {} + //! Constructor from a Point + __forceinline HPoint(const Point& p, float _w=0.0f) : Point(p), w(_w) {} + //! Destructor + __forceinline ~HPoint() {} + + //! Clear the point + __forceinline HPoint& Zero() { x = y = z = w = 0.0f; return *this; } + + //! Assignment from values + __forceinline HPoint& Set(float _x, float _y, float _z, float _w ) { x = _x; y = _y; z = _z; w = _w; return *this; } + //! Assignment from array + __forceinline HPoint& Set(float f[4]) { x = f[0]; y = f[1]; z = f[2]; w = f[3]; return *this; } + //! Assignment from another h-point + __forceinline HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; } + + //! Add a vector + __forceinline HPoint& Add(float _x, float _y, float _z, float _w ) { x += _x; y += _y; z += _z; w += _w; return *this; } + //! Add a vector + __forceinline HPoint& Add(float f[4]) { x += f[0]; y += f[1]; z += f[2]; w += f[3]; return *this; } + + //! Subtract a vector + __forceinline HPoint& Sub(float _x, float _y, float _z, float _w ) { x -= _x; y -= _y; z -= _z; w -= _w; return *this; } + //! Subtract a vector + __forceinline HPoint& Sub(float f[4]) { x -= f[0]; y -= f[1]; z -= f[2]; w -= f[3]; return *this; } + + //! Multiplies by a scalar + __forceinline HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } + + //! Returns MIN(x, y, z, w); + float Min() const { return MIN(x, MIN(y, MIN(z, w))); } + //! Returns MAX(x, y, z, w); + float Max() const { return MAX(x, MAX(y, MAX(z, w))); } + //! TO BE DOCUMENTED + HPoint& Min(const HPoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w); return *this; } + //! TO BE DOCUMENTED + HPoint& Max(const HPoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w); return *this; } + + //! Computes square magnitude + __forceinline float SquareMagnitude() const { return x*x + y*y + z*z + w*w; } + //! Computes magnitude + __forceinline float Magnitude() const { return sqrtf(x*x + y*y + z*z + w*w); } + + //! Normalize the vector + __forceinline HPoint& Normalize() + { + 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; + __forceinline HPoint operator-() const { return HPoint(-x, -y, -z, -w); } + + //! Operator for HPoint Plus = HPoint + HPoint; + __forceinline HPoint operator+(const HPoint& p) const { return HPoint(x + p.x, y + p.y, z + p.z, w + p.w); } + //! Operator for HPoint Minus = HPoint - HPoint; + __forceinline HPoint operator-(const HPoint& p) const { return HPoint(x - p.x, y - p.y, z - p.z, w - p.w); } + + //! Operator for HPoint Mul = HPoint * HPoint; + __forceinline HPoint operator*(const HPoint& p) const { return HPoint(x * p.x, y * p.y, z * p.z, w * p.w); } + //! Operator for HPoint Scale = HPoint * float; + __forceinline HPoint operator*(float s) const { return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float * HPoint; + __forceinline friend HPoint operator*(float s, const HPoint& p) { return HPoint(s * p.x, s * p.y, s * p.z, s * p.w); } + + //! Operator for HPoint Div = HPoint / HPoint; + __forceinline HPoint operator/(const HPoint& p) const { return HPoint(x / p.x, y / p.y, z / p.z, w / p.w); } + //! Operator for HPoint Scale = HPoint / float; + __forceinline HPoint operator/(float s) const { s = 1.0f / s; return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float / HPoint; + __forceinline friend HPoint operator/(float s, const HPoint& p) { return HPoint(s / p.x, s / p.y, s / p.z, s / p.w); } + + //! Operator for float DotProd = HPoint | HPoint; + __forceinline float operator|(const HPoint& p) const { return x*p.x + y*p.y + z*p.z + w*p.w; } + // No cross-product in 4D + + //! Operator for HPoint += HPoint; + __forceinline HPoint& operator+=(const HPoint& p) { x += p.x; y += p.y; z += p.z; w += p.w; return *this; } + //! Operator for HPoint += float; + __forceinline HPoint& operator+=(float s) { x += s; y += s; z += s; w += s; return *this; } + + //! Operator for HPoint -= HPoint; + __forceinline HPoint& operator-=(const HPoint& p) { x -= p.x; y -= p.y; z -= p.z; w -= p.w; return *this; } + //! Operator for HPoint -= float; + __forceinline HPoint& operator-=(float s) { x -= s; y -= s; z -= s; w -= s; return *this; } + + //! Operator for HPoint *= HPoint; + __forceinline HPoint& operator*=(const HPoint& p) { x *= p.x; y *= p.y; z *= p.z; w *= p.w; return *this; } + //! Operator for HPoint *= float; + __forceinline HPoint& operator*=(float s) { x*=s; y*=s; z*=s; w*=s; return *this; } + + //! Operator for HPoint /= HPoint; + __forceinline HPoint& operator/=(const HPoint& p) { x /= p.x; y /= p.y; z /= p.z; w /= p.w; return *this; } + //! Operator for HPoint /= float; + __forceinline HPoint& operator/=(float s) { s = 1.0f / s; x*=s; y*=s; z*=s; w*=s; return *this; } + + // Arithmetic operators + //! Operator for Point Mul = HPoint * Matrix3x3; +// Point operator*(const Matrix3x3& mat) const; + //! Operator for HPoint Mul = HPoint * Matrix4x4; +// HPoint operator*(const Matrix4x4& mat) const; + + // HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + //! Operator for HPoint *= Matrix4x4 +// HPoint& operator*=(const Matrix4x4& mat); + + // Cast operators + //! Cast a HPoint to a Point. w is discarded. + __forceinline operator Point() const { return Point(x, y, z); } + + public: + float w; + }; + +#endif // __ICEHPOINT_H__ + diff --git a/OpcodeDistrib/OPC_Matrix3x3.cpp b/OpcodeDistrib/IceMatrix3x3.cpp similarity index 69% rename from OpcodeDistrib/OPC_Matrix3x3.cpp rename to OpcodeDistrib/IceMatrix3x3.cpp index bf758e8..9582cd8 100644 --- a/OpcodeDistrib/OPC_Matrix3x3.cpp +++ b/OpcodeDistrib/IceMatrix3x3.cpp @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains code for 3x3 matrices. - * \file OPC_Matrix3x3.cpp + * \file IceMatrix3x3.cpp * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -36,7 +28,7 @@ * \class Matrix3x3 * \author Pierre Terdiman * \version 1.0 - * \warning a lot of code has been removed from the standard ICE version + * \warning THIS IS ONLY A VERY LITTLE SUBSET OF THE ORIGINAL ICE CLASS */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/OpcodeDistrib/IceMatrix3x3.h b/OpcodeDistrib/IceMatrix3x3.h new file mode 100644 index 0000000..c2c9d67 --- /dev/null +++ b/OpcodeDistrib/IceMatrix3x3.h @@ -0,0 +1,530 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX3X3_H__ +#define __ICEMATRIX3X3_H__ + + #define MATRIX3X3_EPSILON (1.0e-7f) + #define ROW *(*this) + + class ICEMATHS_API Matrix3x3 + { + public: + //! Empty constructor + __forceinline Matrix3x3() {} + //! Constructor from 9 values + __forceinline 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 + __forceinline Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); } + //! Destructor + __forceinline ~Matrix3x3() {} + + //! Assign values + __forceinline Matrix3x3& 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; + } + + //! Sets the scale from a Point. The point is put on the diagonal. + __forceinline Matrix3x3& SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; return *this; } + + //! Sets the scale from floats. Values are put on the diagonal. + __forceinline Matrix3x3& SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; return *this; } + + //! Scales from a Point. Each row is multiplied by a component. + __forceinline Matrix3x3& 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; + return *this; + } + + //! Scales from floats. Each row is multiplied by a value. + __forceinline Matrix3x3& 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; + return *this; + } + + //! Copy from a Matrix3x3 + __forceinline Matrix3x3& Copy(const Matrix3x3& source) + { + CopyMemory(m, source.m, 9*sizeof(float)); + return *this; + } + + // Row-column access + //! Returns a row. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline float Trace() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + __forceinline Matrix3x3& Zero() { ZeroMemory(&m, sizeof(m)); return *this; } + //! Sets the identity matrix. + __forceinline Matrix3x3& Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; return *this; } + //! Checks for identity + __forceinline 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; + } + + //! 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 ] + __forceinline Matrix3x3& 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; + + return *this; + } + + //! Negates the matrix + __forceinline Matrix3x3& 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]; + return *this; + } + + //! Neg from another matrix + __forceinline Matrix3x3& 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]; + return *this; + } + + //! Add another matrix + __forceinline Matrix3x3& 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]; + return *this; + } + + //! Sub another matrix + __forceinline Matrix3x3& 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]; + return *this; + } + //! Mac + __forceinline Matrix3x3& 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; + + return *this; + } + //! Mac + __forceinline Matrix3x3& 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; + return *this; + } + + //! this = A * s + __forceinline Matrix3x3& 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; + return *this; + } + + __forceinline Matrix3x3& 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]; + return *this; + } + + __forceinline Matrix3x3& 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]; + return *this; + } + + //! this = a * b + __forceinline Matrix3x3& 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]; + return *this; + } + + //! this = transpose(a) * b + __forceinline Matrix3x3& 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]; + return *this; + } + + //! this = a * transpose(b) + __forceinline Matrix3x3& 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]; + return *this; + } + + //! 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. + Matrix3x3& 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; return *this; } + //! Set a rotation matrix around the Y axis. + Matrix3x3& 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; return *this; } + //! Set a rotation matrix around the Z axis. + Matrix3x3& 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; return *this; } + + //! Make a rotation matrix about an arbitrary angle + Matrix3x3& Rot(float angle, const Point& axis); + + //! Transpose the matrix. + Matrix3x3& 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]); + return *this; + } + + //! this = Transpose(a) + Matrix3x3& 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]; + return *this; + } + + //! 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 + Matrix3x3 Temp; + + float OneOverDet = 1.0f / Det; + + 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() + { + Point RowX, RowY, RowZ; + GetRow(0, RowX); +// GetRow(1, RowY); + GetRow(2, RowZ); + RowZ.Normalize(); + RowY = (RowZ ^ RowX).Normalize(); + RowX = RowY ^ RowZ; + SetRow(0, RowX); + SetRow(1, RowY); + SetRow(2, RowZ); +// this->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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline Point operator*(const Point& v) const { return Point(ROW[0]|v, ROW[1]|v, ROW[2]|v); } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * float; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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 + __forceinline 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 + __forceinline 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 + __forceinline 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 + __forceinline 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 + __forceinline 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; + + __forceinline const Point* operator[](int nRow) const { return (const Point*)&m[nRow][0]; } + __forceinline Point* operator[](int nRow) { return (Point*)&m[nRow][0]; } + + public: + + float m[3][3]; +/* + union{ + struct{ + //! Direct access to rows + Point mRow[3]; + }; + //! Can also be defined by an array of 9 floats. + float m[3][3]; + }; +*/ + }; + +#endif // __ICEMATRIX3X3_H__ + diff --git a/OpcodeDistrib/OPC_Matrix4x4.cpp b/OpcodeDistrib/IceMatrix4x4.cpp similarity index 77% rename from OpcodeDistrib/OPC_Matrix4x4.cpp rename to OpcodeDistrib/IceMatrix4x4.cpp index c7fd768..54cf553 100644 --- a/OpcodeDistrib/OPC_Matrix4x4.cpp +++ b/OpcodeDistrib/IceMatrix4x4.cpp @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains code for 4x4 matrices. - * \file OPC_Matrix4x4.cpp + * \file IceMatrix4x4.cpp * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -39,7 +31,7 @@ * \class Matrix4x4 * \author Pierre Terdiman * \version 1.0 - * \warning a lot of code has been removed from the standard ICE version + * \warning THIS IS ONLY A VERY LITTLE SUBSET OF THE ORIGINAL ICE CLASS */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -48,9 +40,6 @@ // Precompiled Header #include "Stdafx.h" -using namespace Opcode; - -#ifndef __ICEMATHS_H__ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -61,7 +50,7 @@ using namespace Opcode; * \param src [in] source matrix */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -OPCODE_API void Opcode::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) +void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) { dest.m[0][0] = src.m[0][0]; dest.m[1][0] = src.m[0][1]; @@ -83,5 +72,3 @@ OPCODE_API void Opcode::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) dest.m[2][3] = 0.0f; dest.m[3][3] = 1.0f; } - -#endif // __ICEMATHS_H__ diff --git a/OpcodeDistrib/IceMatrix4x4.h b/OpcodeDistrib/IceMatrix4x4.h new file mode 100644 index 0000000..1a6f2d0 --- /dev/null +++ b/OpcodeDistrib/IceMatrix4x4.h @@ -0,0 +1,460 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX4X4_H__ +#define __ICEMATRIX4X4_H__ + + #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. + __forceinline Matrix4x4() {} + //! Constructor from 16 values + __forceinline 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 + __forceinline Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } + //! Destructor. + __forceinline ~Matrix4x4() {} + + //! Assign values + __forceinline 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. + __forceinline const HPoint* GetTrans() const { return (*this)[3];/*(Point*) &mTrans;*/ } + //! Gets the translation part of the matrix + __forceinline const Matrix4x4& GetTrans(Point& p) const { p.x = m[3][0]; p.y = m[3][1]; p.z = m[3][2]; return *this; } + //! Sets the translation part of the matrix, from a Point. + __forceinline Matrix4x4& SetTrans(const Point& p) { m[3][0] = p.x; m[3][1] = p.y; m[3][2] = p.z; return *this; } + //! Sets the translation part of the matrix, from a HPoint. + __forceinline Matrix4x4& SetTrans(const HPoint& p) { m[3][0] = p.x; m[3][1] = p.y; m[3][2] = p.z; m[3][3] = p.w; return *this; } + //! Sets the translation part of the matrix, from floats. + __forceinline Matrix4x4& SetTrans(float tx, float ty, float tz) { m[3][0] = tx; m[3][1] = ty; m[3][2] = tz; return *this; } + + // Scale + //! Sets the scale from a Point. The point is put on the diagonal. + __forceinline Matrix4x4& SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; return *this; } + //! Sets the scale from floats. Values are put on the diagonal. + __forceinline Matrix4x4& SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; return *this; } + //! Scales from a Point. Each row is multiplied by a component. + Matrix4x4& 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; + return *this; + } + //! Scales from floats. Each row is multiplied by a value. + Matrix4x4& 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; + return *this; + } + + //! Copy from a Matrix4x4 + __forceinline Matrix4x4& Copy(const Matrix4x4& source) + { + CopyMemory(m, source.m, 16*sizeof(float)); + return *this; + } + + // Row-column access + //! Returns a row. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline 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. + __forceinline HPoint GetRow(const udword row) const { return mRow[row]; } + //! Sets a row. + __forceinline 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. + __forceinline float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; } + //! Clears the matrix. + __forceinline Matrix4x4& Zero() { ZeroMemory(&m, sizeof(m)); return *this; } + //! Sets the identity matrix. + __forceinline Matrix4x4& Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; return *this; } + //! Checks for identity + __forceinline 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. + Matrix4x4& 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; return *this; } + //! Sets a rotation matrix around the Y axis. + Matrix4x4& 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; return *this; } + //! Sets a rotation matrix around the Z axis. + Matrix4x4& 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; return *this; } + + //! Makes a rotation matrix about an arbitrary angle + Matrix4x4& Rot(float angle, Point& p1, Point& p2); + + //! Transposes the matrix. + Matrix4x4& 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]); + return *this; + } + + //! 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. + __forceinline 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; +// operator PRS() const; + + // Arithmetic operators + //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + __forceinline 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; + } + + __forceinline const HPoint* operator[](int nRow) const { return (const HPoint*)&m[nRow][0]; } + __forceinline HPoint* operator[](int nRow) { return (HPoint*)&m[nRow][0]; } + + public: + + float m[4][4]; +/* + union{ + struct{ + //! Direct access to rows. + HPoint mRow[3]; + //! The translation part. + HPoint mTrans; + }; + //! Can also be defined by an array of 16 floats. + float m[4][4]; + }; +*/ + }; + + //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix + __forceinline 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]; + } + + ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); + + class ICEMATHS_API MatrixPalette : public Container + { + public: + // Constructor / Destructor + __forceinline MatrixPalette() {} + __forceinline ~MatrixPalette() {} + + __forceinline udword GetNbMatrices() const { return GetNbEntries() / (sizeof(Matrix4x4)/sizeof(float)); } + __forceinline Matrix4x4* GetMatrices() const { return (Matrix4x4*)GetEntries(); } + + void AddMatrix(const Matrix4x4& mat) { float* f = (float*)mat.m; for(udword i=0;i<16;i++) Add(f[i]); } + }; + +#endif // __ICEMATRIX4X4_H__ + diff --git a/OpcodeDistrib/IceMemoryMacros.h b/OpcodeDistrib/IceMemoryMacros.h new file mode 100644 index 0000000..3e22d9a --- /dev/null +++ b/OpcodeDistrib/IceMemoryMacros.h @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains all memory macros. + * \file IceMemoryMacros.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMEMORYMACROS_H__ +#define __ICEMEMORYMACROS_H__ + +#undef ZeroMemory +#undef CopyMemory +#undef MoveMemory +#undef FillMemory + +//! A function to clear a buffer. +//! \param addr buffer address +//! \param size buffer length +//! \see FillMemory +//! \see CopyMemory +//! \see MoveMemory +ICECORE_API __forceinline void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } + +//! A function to fill a buffer with a given byte. +//! \param addr buffer address +//! \param size buffer length +//! \param val the byte value +//! \see ZeroMemory +//! \see CopyMemory +//! \see MoveMemory +ICECORE_API __forceinline void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } + +//! A function to copy a buffer. +//! \param addr destination buffer address +//! \param addr source buffer address +//! \param size buffer length +//! \see ZeroMemory +//! \see FillMemory +//! \see MoveMemory +ICECORE_API __forceinline void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); } + +//! A function to move a buffer. +//! \param addr destination buffer address +//! \param addr source buffer address +//! \param size buffer length +//! \see ZeroMemory +//! \see FillMemory +//! \see CopyMemory +ICECORE_API __forceinline void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); } + +#define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)"). +//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE. +#define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class. +#define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array. +#define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release +#define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release +#define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUTOFMEMORY); //!< Standard alloc checking. HANDLE WITH CARE. + +#endif // __ICEMEMORYMACROS_H__ diff --git a/OpcodeDistrib/IcePlane.h b/OpcodeDistrib/IcePlane.h new file mode 100644 index 0000000..dc3cc80 --- /dev/null +++ b/OpcodeDistrib/IcePlane.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for planes. + * \file IcePlane.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPLANE_H__ +#define __ICEPLANE_H__ + + #define PLANE_EPSILON (1.0e-7f) + + class ICEMATHS_API Plane + { + public: + // Constructors, destructor, copy constructor + __forceinline Plane() {} + __forceinline Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); } + __forceinline Plane(const Point &p, const Point &n) { Set(p, n); } + __forceinline Plane(const Point &p0, const Point &p1, const Point &p2) { Compute(p0, p1, p2); } + __forceinline Plane(const Point &n, float d) { this->n = n; this->d = d; } + + __forceinline Plane& Zero() { n.Zero(); d = 0.0f; return *this; } + __forceinline Plane& Set(float nx, float ny, float nz, float d) { n.Set(nx, ny, nz); this->d = d; return *this; } + __forceinline Plane& Set(const Point &p, const Point &n) { this->n = n; d = - p | n; return *this; } + Plane& Compute(const Point &p0, const Point &p1, const Point &p2); + + __forceinline float Distance(const Point& p) const { return (p | n) + d; } + __forceinline bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; } + + __forceinline void Normalize() + { + 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 + __forceinline operator Point() const { return n; } + __forceinline operator HPoint() const { return HPoint(n, d); } + + // Arithmetic operators + __forceinline Plane operator+(const Plane& p) const { return Plane(n + p.n, d + p.d); } + __forceinline Plane operator*(const Matrix4x4& m) const { Plane Ret(*this); return Ret *= m; } + + __forceinline Plane& operator+=(const Plane& p) { n += p.n; n.Normalize(); d += p.d; return *this; } // Is this really useful, Piotr? + __forceinline Plane& operator*=(const Matrix4x4& m) { Point n2 = HPoint(n, 0.0f ) * m; d = -((Point) (HPoint( -d*n, 1.0f ) * m) | n2); n = n2; return *this; } + }; + +#endif // __ICEPLANE_H__ diff --git a/OpcodeDistrib/OPC_Point.cpp b/OpcodeDistrib/IcePoint.cpp similarity index 83% rename from OpcodeDistrib/OPC_Point.cpp rename to OpcodeDistrib/IcePoint.cpp index 8bc939a..63b50e0 100644 --- a/OpcodeDistrib/OPC_Point.cpp +++ b/OpcodeDistrib/IcePoint.cpp @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains code for 3D vectors. - * \file OPC_Point.cpp + * \file IcePoint.cpp * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -22,7 +14,7 @@ * \class Point * \author Pierre Terdiman * \version 1.0 - * \warning a lot of code has been removed from the standard ICE version + * \warning THIS IS ONLY A VERY LITTLE SUBSET OF THE ORIGINAL ICE CLASS */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -30,9 +22,8 @@ // Precompiled Header #include "Stdafx.h" -using namespace Opcode; - -#ifndef __ICEMATHS_H__ +// Cast operator +Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Point Mul = Point * Matrix3x3; @@ -83,5 +74,3 @@ Point& Point::operator*=(const Matrix4x4& mat) return *this; } - -#endif // __ICEMATHS_H__ diff --git a/OpcodeDistrib/IcePoint.h b/OpcodeDistrib/IcePoint.h new file mode 100644 index 0000000..de729d3 --- /dev/null +++ b/OpcodeDistrib/IcePoint.h @@ -0,0 +1,437 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3D vectors. + * \file IcePoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPOINT_H__ +#define __ICEPOINT_H__ + + class Plane; + class Matrix3x3; + + #define CROSS2D(a, b) (a.x*b.y - b.x*a.y) + + const float EPSILON2 = 1.0e-20f; + + class ICEMATHS_API Point + { + public: + + //! Empty constructor + __forceinline Point() {} + //! Constructor from floats + __forceinline Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + //! Constructor from array + __forceinline Point(float f[3]) : x(f[0]), y(f[1]), z(f[2]) {} + //! Copy constructor + __forceinline Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} + //! Destructor + __forceinline ~Point() {} + + //! Clear vector + __forceinline Point& Zero() { x = y = z = 0.0f; return *this; } + + //! + infinity + __forceinline Point& SetPlusInfinity() { x = y = z = MAX_FLOAT; return *this; } + //! - infinity + __forceinline Point& SetMinusInfinity() { x = y = z = MIN_FLOAT; return *this; } + + //! Set unit random vector + Point& UnitRandomVector() + { + x = UnitRandomFloat(); + y = UnitRandomFloat(); + z = UnitRandomFloat(); + Normalize(); + return *this; + } + //! Assignment from values + __forceinline Point& Set(float _x, float _y, float _z) { x = _x; y = _y; z = _z; return *this; } + //! Assignment from array + __forceinline Point& Set(float f[3]) { x = f[0]; y = f[1]; z = f[2]; return *this; } + //! Assignment from another point + __forceinline Point& Set(const Point& src) { x = src.x; y = src.y; z = src.z; return *this; } + + //! Add a vector + __forceinline Point& Add(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Add a vector + __forceinline Point& Add(float _x, float _y, float _z) { x += _x; y += _y; z += _z; return *this; } + //! Add a vector + __forceinline Point& Add(float f[3]) { x += f[0]; y += f[1]; z += f[2]; return *this; } + //! Add vectors + __forceinline 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; } + + //! Subtract a vector + __forceinline Point& Sub(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Subtract a vector + __forceinline Point& Sub(float _x, float _y, float _z) { x -= _x; y -= _y; z -= _z; return *this; } + //! Subtract a vector + __forceinline Point& Sub(float f[3]) { x -= f[0]; y -= f[1]; z -= f[2]; return *this; } + //! Subtract vectors + __forceinline 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; } + + //! this = -this + __forceinline Point& Neg() { x = -x; y = -y; z = -z; return *this;} + //! this = -a + __forceinline Point& Neg(const Point& a) { x = -a.x; y = -a.y; z = -a.z; return *this;} + + //! Multiplies by a scalar + __forceinline Point& Mult(float s) { x *= s; y *= s; z *= s; return *this; } + + //! this = a * scalar + __forceinline Point& Mult(const Point& a, float scalar) + { + x = a.x * scalar; + y = a.y * scalar; + z = a.z * scalar; + return *this; + } + + //! this = a + b * scalar + __forceinline Point& Mac(const Point& a, const Point& b, float scalar) + { + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; + z = a.z + b.z * scalar; + return *this; + } + + //! this = this + a * scalar + __forceinline Point& Mac(const Point& a, float scalar) + { + x += a.x * scalar; + y += a.y * scalar; + z += a.z * scalar; + return *this; + } + + //! this = a - b * scalar + __forceinline Point& Msc(const Point& a, const Point& b, float scalar) + { + x = a.x - b.x * scalar; + y = a.y - b.y * scalar; + z = a.z - b.z * scalar; + return *this; + } + + //! this = this - a * scalar + __forceinline Point& Msc(const Point& a, float scalar) + { + x -= a.x * scalar; + y -= a.y * scalar; + z -= a.z * scalar; + return *this; + } + + //! this = a + b * scalarb + c * scalarc + __forceinline Point& Mac2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x + b.x * scalarb + c.x * scalarc; + y = a.y + b.y * scalarb + c.y * scalarc; + z = a.z + b.z * scalarb + c.z * scalarc; + return *this; + } + + //! this = a - b * scalarb - c * scalarc + __forceinline Point& Msc2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x - b.x * scalarb - c.x * scalarc; + y = a.y - b.y * scalarb - c.y * scalarc; + z = a.z - b.z * scalarb - c.z * scalarc; + return *this; + } + + //! this = mat * a + __forceinline Point& Mult(const Matrix3x3& mat, const Point& a); + + //! this = mat1 * a1 + mat2 * a2 + __forceinline Point& Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2); + + //! this = this + mat * a + __forceinline Point& Mac(const Matrix3x3& mat, const Point& a); + + //! this = transpose(mat) * a + __forceinline Point& TransMult(const Matrix3x3& mat, const Point& a); + + //! Linear interpolate between two vectors: this = a + t * (b - a) + __forceinline Point& Lerp(const Point& a, const Point& b, float t) + { + x = a.x + t * (b.x - a.x); + y = a.y + t * (b.y - a.y); + z = a.z + t * (b.z - a.z); + return *this; + } + + //! Hermite interpolate between p1 and p2. p0 and p3 are used for finding gradient at p1 and p2. + //! this = p0 * (2t^2 - t^3 - t)/2 + //! + p1 * (3t^3 - 5t^2 + 2)/2 + //! + p2 * (4t^2 - 3t^3 + t)/2 + //! + p3 * (t^3 - t^2)/2 + __forceinline Point& Herp(const Point& p0, const Point& p1, const Point& p2, const Point& p3, float t) + { + float t2 = t * t; + float t3 = t2 * t; + float kp0 = (2.0f * t2 - t3 - t) * 0.5f; + float kp1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f; + float kp2 = (4.0f * t2 - 3.0f * t3 + t) * 0.5f; + float kp3 = (t3 - t2) * 0.5f; + x = p0.x * kp0 + p1.x * kp1 + p2.x * kp2 + p3.x * kp3; + y = p0.y * kp0 + p1.y * kp1 + p2.y * kp2 + p3.y * kp3; + z = p0.z * kp0 + p1.z * kp1 + p2.z * kp2 + p3.z * kp3; + return *this; + } + + //! this = rotpos * r + linpos + __forceinline Point& Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! this = trans(rotpos) * (r - linpos) + __forceinline Point& InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! Returns MIN(x, y, z); + __forceinline float Min() const { return MIN(x, MIN(y, z)); } + //! Returns MAX(x, y, z); + __forceinline float Max() const { return MAX(x, MAX(y, z)); } + //! TO BE DOCUMENTED + __forceinline Point& Min(const Point& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; } + //! TO BE DOCUMENTED + __forceinline Point& Max(const Point& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; } + + //! Computes square magnitude + __forceinline float SquareMagnitude() const { return x*x + y*y + z*z; } + //! Computes magnitude + __forceinline float Magnitude() const { return sqrtf(x*x + y*y + z*z); } + + //! A method to check the point is near zero + bool ApproxZero() const { return SquareMagnitude() < EPSILON2; } + + //! A method to slighty move the point + void Tweak(udword coordmask, udword tweakmask) + { + 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); } + } + + #define TWEAKMASK 0x3fffff + #define TWEAKNOTMASK ~TWEAKMASK + //! A method to slighty move the point out + __forceinline void TweakBigger() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! A method to slighty move the point in + __forceinline void TweakSmaller() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! Normalize the vector + __forceinline Point& Normalize() + { + float M = x*x + y*y + z*z; + if(M) + { + M = 1.0f / sqrtf(M); + x *= M; + y *= M; + z *= M; + } + return *this; + } + + //! Set vector length + __forceinline Point& SetLength(float length) + { + float NewLength = length / sqrtf(x*x + y*y + z*z); + x *= NewLength; + y *= NewLength; + z *= NewLength; + return *this; + } + + //! Compute distance to another point + __forceinline float Distance(const Point& b) const + { + return sqrtf((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Compute square distance to another point + __forceinline float SquareDistance(const Point& b) const + { + return ((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Dot product dp = this|a + __forceinline float Dot(const Point& p) const { return p.x * x + p.y * y + p.z * z; } + + //! Cross product this = a x b + __forceinline Point& Cross(const Point& a, const Point& b) + { + x = a.y * b.z - a.z * b.y; + y = a.z * b.x - a.x * b.z; + z = a.x * b.y - a.y * b.x; + return *this; + } + + //! Vector code ( bitmask = sign(z) | sign(y) | sign(x) ) + __forceinline udword VectorCode() const + { + return (IR(x)>>31) | ((IR(y)&SIGN_BITMASK)>>30) | ((IR(z)&SIGN_BITMASK)>>29); + } + + //! Return largest axis + __forceinline udword LargestAxis() const + { + const float* Vals = &x; + udword m = 0; + if(Vals[1] > Vals[m]) m = 1; + if(Vals[2] > Vals[m]) m = 2; + return m; + } + + //! Return closest axis + __forceinline udword ClosestAxis() const + { + const float* Vals = &x; + udword m = 0; + if(AIR(Vals[1]) > AIR(Vals[m])) m = 1; + if(AIR(Vals[2]) > AIR(Vals[m])) m = 2; + return m; + } + + //! Return smallest axis + __forceinline udword SmallestAxis() const + { + const float* Vals = &x; + udword m = 0; + if(Vals[1] < Vals[m]) m = 1; + if(Vals[2] < Vals[m]) m = 2; + return m; + } + + //! Refract the point + Point& Refract(Point& eye, Point& n, float refractindex, Point& refracted); + + //! Project the point onto a plane + Point& ProjectToPlane(const Plane& p); + + //! Project the point onto the screen + void ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const; + + //! Unfold the point onto a plane according to edge(a,b) + Point& Unfold(Plane& p, Point& a, Point& b); + + // Arithmetic operators + //! Operator for Point Negate = - Point + __forceinline Point operator-() const { return Point(-x, -y, -z); } + + //! Operator for Point Plus = Point + Point. + __forceinline Point operator+(const Point& p) const { return Point(x + p.x, y + p.y, z + p.z); } + //! Operator for Point Minus = Point - Point. + __forceinline Point operator-(const Point& p) const { return Point(x - p.x, y - p.y, z - p.z); } + + //! Operator for Point Mul = Point * Point. + __forceinline Point operator*(const Point& p) const { return Point(x * p.x, y * p.y, z * p.z); } + //! Operator for Point Scale = Point * float. + __forceinline Point operator*(float s) const { return Point(x * s, y * s, z * s ); } + //! Operator for Point Scale = float * Point. + __forceinline friend Point operator*(float s, const Point& p) { return Point(s * p.x, s * p.y, s * p.z); } + + //! Operator for Point Div = Point / Point. + __forceinline Point operator/(const Point& p) const { return Point(x / p.x, y / p.y, z / p.z); } + //! Operator for Point Scale = Point / float. + __forceinline Point operator/(float s) const { s = 1.0f / s; return Point(x * s, y * s, z * s); } + //! Operator for Point Scale = float / Point. + __forceinline friend Point operator/(float s, const Point& p) { return Point(s / p.x, s / p.y, s / p.z); } + + //! Operator for float DotProd = Point | Point. + __forceinline float operator|(const Point& p) const { return x*p.x + y*p.y + z*p.z; } + //! Operator for Point VecProd = Point ^ Point. + __forceinline Point operator^(const Point& p) const + { + return Point( + y * p.z - z * p.y, + z * p.x - x * p.z, + x * p.y - y * p.x ); + } + + //! Operator for Point += Point. + __forceinline Point& operator+=(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Operator for Point += float. + __forceinline Point& operator+=(float s) { x += s; y += s; z += s; return *this; } + + //! Operator for Point -= Point. + __forceinline Point& operator-=(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Operator for Point -= float. + __forceinline Point& operator-=(float s) { x -= s; y -= s; z -= s; return *this; } + + //! Operator for Point *= Point. + __forceinline Point& operator*=(const Point& p) { x *= p.x; y *= p.y; z *= p.z; return *this; } + //! Operator for Point *= float. + __forceinline Point& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } + + //! Operator for Point /= Point. + __forceinline Point& operator/=(const Point& p) { x /= p.x; y /= p.y; z /= p.z; return *this; } + //! Operator for Point /= float. + __forceinline Point& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; } + + // Arithmetic operators + //! Operator for Point Mul = Point * Matrix3x3. + Point operator*(const Matrix3x3& mat) const; + //! Operator for Point Mul = Point * Matrix4x4. + Point operator*(const Matrix4x4& mat) const; + + //! Operator for Point *= Matrix3x3. + Point& operator*=(const Matrix3x3& mat); + //! Operator for Point *= Matrix4x4. + Point& operator*=(const Matrix4x4& mat); + + // Cast operators + //! Cast a Point to a HPoint. w is set to zero. + operator HPoint() const; + + __forceinline operator const float*() const { return &x; } + __forceinline operator float*() { return &x; } + + public: + float x, y, z; +/* + union{ + struct{ + //! Could be a vertex or texture-vertex + float x, y, z; + }; + struct{ + //! Could be a vertex-color + float r, g, b; + }; + //! Array access + float m[3]; + };*/ + }; + + class ICEMATHS_API Vertices : public Container + { + public: + // Constructor / Destructor + __forceinline Vertices() {} + __forceinline ~Vertices() {} + + __forceinline udword GetNbVertices() const { return GetNbEntries()/3; } + __forceinline Point* GetVertices() const { return (Point*)GetEntries(); } + + Vertices& AddVertex(const Point& p) { Add(p.x).Add(p.y).Add(p.z); return *this; } + }; + +#endif //__ICEPOINT_H__ diff --git a/OpcodeDistrib/IceRandom.h b/OpcodeDistrib/IceRandom.h new file mode 100644 index 0000000..012d2a9 --- /dev/null +++ b/OpcodeDistrib/IceRandom.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.h + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERANDOM_H__ +#define __ICERANDOM_H__ + + //! Returns a unit random floating-point value + __forceinline float UnitRandomFloat() { return float(rand()) * ONE_OVER_RAND_MAX; } + + class ICECORE_API BasicRandom + { + public: + + //! Constructor + __forceinline BasicRandom(udword seed=0) : mRnd(seed) {} + //! Destructor + __forceinline ~BasicRandom() {} + + __forceinline void SetSeed(udword seed) { mRnd = seed; } + __forceinline udword GetCurrentValue() const { return mRnd; } + __forceinline udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; } + + private: + udword mRnd; + }; + +#endif // __ICERANDOM_H__ + diff --git a/OpcodeDistrib/IceRay.h b/OpcodeDistrib/IceRay.h new file mode 100644 index 0000000..6175546 --- /dev/null +++ b/OpcodeDistrib/IceRay.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 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 + __forceinline Ray() {} + //! Destructor + __forceinline ~Ray() {} + + Point mOrig; //!< Ray origin + Point mDir; //!< Normed direction + }; + + class ICEMATHS_API Segment + { + public: + //! Constructor + __forceinline Segment() {} + //! Destructor + __forceinline ~Segment() {} + + Point mP0; //!< Start of segment + Point mP1; //!< End of segment + }; + + __forceinline void ComputeReflexionVector(Point& reflected, const Point& source, const Point& impact, const Point& normal) + { + Point V = impact - source; + reflected = V - normal * 2.0f * (V|normal); + } + + __forceinline void ComputeReflexionVector(Point& reflected, const Point& dir, const Point& normal) + { + reflected = dir - normal * 2.0f * (dir|normal); + } + +#endif // __ICERAY_H__ diff --git a/OpcodeDistrib/OPC_Triangle.cpp b/OpcodeDistrib/IceTriangle.cpp similarity index 71% rename from OpcodeDistrib/OPC_Triangle.cpp rename to OpcodeDistrib/IceTriangle.cpp index 8dd0c92..2c0bff0 100644 --- a/OpcodeDistrib/OPC_Triangle.cpp +++ b/OpcodeDistrib/IceTriangle.cpp @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains a triangle class. - * \file OPC_Triangle.cpp + * Contains a handy triangle class. + * \file IceTriangle.cpp * \author Pierre Terdiman * \date January, 17, 2000 */ @@ -19,25 +11,21 @@ // Precompiled Header #include "Stdafx.h" -using namespace Opcode; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains an indexed triangle class. + * Contains a triangle class. * - * \class Triangle + * \class Tri * \author Pierre Terdiman * \version 1.0 * \date 08.15.98 - * \warning this is a very little subset of the same class in ICE. + * \warning THIS IS ONLY A VERY LITTLE SUBSET OF THE ORIGINAL ICE CLASS */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef __MESHMERIZER_H__ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to compute the triangle center. + * Computes the triangle center. * \param verts [in] the list of indexed vertices * \param center [out] the computed center */ @@ -55,7 +43,7 @@ void Triangle::Center(const Point* verts, Point& center) const /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to check whether the triangle is degenerated or not. A degenerated triangle has two common vertex references. This is a zero-area triangle. + * Checks whether the triangle is degenerated or not. A degenerated triangle has two common vertex references. This is a zero-area triangle. * \return true if the triangle is degenerated */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,5 +54,3 @@ bool Triangle::IsDegenerate() const if(mVRef[2]==mVRef[0]) return true; return false; } - -#endif \ No newline at end of file diff --git a/OpcodeDistrib/IceTriangle.h b/OpcodeDistrib/IceTriangle.h new file mode 100644 index 0000000..0f85803 --- /dev/null +++ b/OpcodeDistrib/IceTriangle.h @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 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 Triangle + { + public: + //! Constructor + __forceinline Triangle() {} + //! Constructor + __forceinline Triangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; } + //! Destructor + __forceinline ~Triangle() {} + //! Vertex-references + udword mVRef[3]; + + // Methods + void Center(const Point* verts, Point& center) const; + bool IsDegenerate() const; + }; + +#endif // __ICETRIANGLE_H__ diff --git a/OpcodeDistrib/OPC_Types.h b/OpcodeDistrib/IceTypes.h similarity index 78% rename from OpcodeDistrib/OPC_Types.h rename to OpcodeDistrib/IceTypes.h index 323c676..7968ce8 100644 --- a/OpcodeDistrib/OPC_Types.h +++ b/OpcodeDistrib/IceTypes.h @@ -1,15 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains custom types. - * \file OPC_Types.h + * \file IceTypes.h * \author Pierre Terdiman * \date April, 4, 2000 */ @@ -20,8 +12,7 @@ #ifndef __ICETYPES_H__ #define __ICETYPES_H__ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // CONSTANTS + // Constants #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI #define HALFPI 1.57079632679489661923f //!< 0.5 * PI #define TWOPI 6.28318530717958647692f //!< 2.0 * PI @@ -31,7 +22,9 @@ #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians #define EXP 2.71828182845904523536f //!< e - #define ILOG2 3.32192809488736234787f //!< 1.0 / log10(2) + #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 @@ -53,6 +46,19 @@ 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(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 @@ -101,6 +107,8 @@ #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() @@ -114,6 +122,11 @@ #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 __forceinline const T& TMin (const T& a, const T& b) { return b < a ? b : a; } + template __forceinline const T& TMax (const T& a, const T& b) { return a < b ? b : a; } + template __forceinline void TSetMin (T& a, const T& b) { if(a>b) a = b; } + template __forceinline void TSetMax (T& a, const T& b) { if(a_BuildHierarchy(builder); } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. @@ -339,6 +345,7 @@ bool AABBTree::Build(AABBTreeBuilder* builder) // Get back total number of nodes mTotalNbNodes = builder->GetCount(); + return true; } @@ -384,4 +391,16 @@ udword AABBTree::GetUsedBytes() const udword TotalSize = mTotalNbNodes*GetNodeSize(); if(mIndices) TotalSize+=mNbPrimitives*sizeof(udword); return TotalSize; -} \ No newline at end of file +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A method to check the tree is a complete tree or not. + * A complete tree is made of 2*N-1 nodes, where N is the number of primitives in the tree. + * \return true for complete trees + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::IsComplete() const +{ + return (GetNbNodes()==GetNbPrimitives()*2-1); +} diff --git a/OpcodeDistrib/OPC_AABBTree.h b/OpcodeDistrib/OPC_AABBTree.h index 0c38e0d..0eb7f7b 100644 --- a/OpcodeDistrib/OPC_AABBTree.h +++ b/OpcodeDistrib/OPC_AABBTree.h @@ -55,7 +55,6 @@ // 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); @@ -74,6 +73,8 @@ __forceinline const udword* GetIndices() const { return mIndices; } //!< Catch the indices __forceinline udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + // Infos + bool IsComplete() const; // Stats udword ComputeDepth() const; udword GetUsedBytes() const; diff --git a/OpcodeDistrib/OPC_BVTCache.h b/OpcodeDistrib/OPC_BVTCache.h new file mode 100644 index 0000000..9b60be6 --- /dev/null +++ b/OpcodeDistrib/OPC_BVTCache.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * 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 + __forceinline 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__ + } + + __forceinline 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_BoxBoxOverlap.h b/OpcodeDistrib/OPC_BoxBoxOverlap.h new file mode 100644 index 0000000..906587f --- /dev/null +++ b/OpcodeDistrib/OPC_BoxBoxOverlap.h @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * OBB-OBB overlap test using the separating axis theorem. + * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID) + * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion) + * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory) + * - Class III axes can be disabled... (SOLID & Intel fashion) + * - ...or enabled to perform some profiling + * - CPU comparisons used when appropriate + * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID) + * + * \param a [in] extents from box A + * \param Pa [in] center from box A + * \param b [in] extents from box B + * \param Pb [in] center from box B + * \return true if boxes overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +__forceinline bool AABBTreeCollider::BoxBoxOverlap(const Point& a, const Point& Pa, const Point& b, const Point& Pb) +{ + // Stats + mNbBVBVTests++; + + float t,t2; + + // Class I : A's basis vectors +#ifdef OPC_CPU_COMPARE + float Tx = (mR1to0.m[0][0]*Pb.x + mR1to0.m[1][0]*Pb.y + mR1to0.m[2][0]*Pb.z) + mT1to0.x - Pa.x; + t = a.x + b.x*mAR.m[0][0] + b.y*mAR.m[1][0] + b.z*mAR.m[2][0]; + if(AIR(Tx) > IR(t)) return false; + + float Ty = (mR1to0.m[0][1]*Pb.x + mR1to0.m[1][1]*Pb.y + mR1to0.m[2][1]*Pb.z) + mT1to0.y - Pa.y; + t = a.y + b.x*mAR.m[0][1] + b.y*mAR.m[1][1] + b.z*mAR.m[2][1]; + if(AIR(Ty) > IR(t)) return false; + + float Tz = (mR1to0.m[0][2]*Pb.x + mR1to0.m[1][2]*Pb.y + mR1to0.m[2][2]*Pb.z) + mT1to0.z - Pa.z; + t = a.z + b.x*mAR.m[0][2] + b.y*mAR.m[1][2] + b.z*mAR.m[2][2]; + if(AIR(Tz) > IR(t)) return false; +#else + float Tx = (mR1to0.m[0][0]*Pb.x + mR1to0.m[1][0]*Pb.y + mR1to0.m[2][0]*Pb.z) + mT1to0.x - Pa.x; + t = a.x + b.x*mAR.m[0][0] + b.y*mAR.m[1][0] + b.z*mAR.m[2][0]; + if(fabsf(Tx) > t) return false; + + float Ty = (mR1to0.m[0][1]*Pb.x + mR1to0.m[1][1]*Pb.y + mR1to0.m[2][1]*Pb.z) + mT1to0.y - Pa.y; + t = a.y + b.x*mAR.m[0][1] + b.y*mAR.m[1][1] + b.z*mAR.m[2][1]; + if(fabsf(Ty) > t) return false; + + float Tz = (mR1to0.m[0][2]*Pb.x + mR1to0.m[1][2]*Pb.y + mR1to0.m[2][2]*Pb.z) + mT1to0.z - Pa.z; + t = a.z + b.x*mAR.m[0][2] + b.y*mAR.m[1][2] + b.z*mAR.m[2][2]; + if(fabsf(Tz) > t) return false; +#endif + + // Class II : B's basis vectors +#ifdef OPC_CPU_COMPARE + t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = a.x*mAR.m[0][0] + a.y*mAR.m[0][1] + a.z*mAR.m[0][2] + b.x; + if(AIR(t)>IR(t2)) return false; + + t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = a.x*mAR.m[1][0] + a.y*mAR.m[1][1] + a.z*mAR.m[1][2] + b.y; + if(AIR(t)>IR(t2)) return false; + + t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = a.x*mAR.m[2][0] + a.y*mAR.m[2][1] + a.z*mAR.m[2][2] + b.z; + if(AIR(t)>IR(t2)) return false; +#else + t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = a.x*mAR.m[0][0] + a.y*mAR.m[0][1] + a.z*mAR.m[0][2] + b.x; + if(fabsf(t) > t2) return false; + + t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = a.x*mAR.m[1][0] + a.y*mAR.m[1][1] + a.z*mAR.m[1][2] + b.y; + if(fabsf(t) > t2) return false; + + t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = a.x*mAR.m[2][0] + a.y*mAR.m[2][1] + a.z*mAR.m[2][2] + b.z; + if(fabsf(t) > t2) return false; +#endif + + // Class III : 9 cross products + // Cool trick: always perform the full test for first level, regardless of settings. + // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! + if(mFullBoxBoxTest || mNbBVBVTests==1) + { +#ifdef OPC_CPU_COMPARE + t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = a.y*mAR.m[0][2] + a.z*mAR.m[0][1] + b.y*mAR.m[2][0] + b.z*mAR.m[1][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B0 + t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = a.y*mAR.m[1][2] + a.z*mAR.m[1][1] + b.x*mAR.m[2][0] + b.z*mAR.m[0][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B1 + t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = a.y*mAR.m[2][2] + a.z*mAR.m[2][1] + b.x*mAR.m[1][0] + b.y*mAR.m[0][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B2 + t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = a.x*mAR.m[0][2] + a.z*mAR.m[0][0] + b.y*mAR.m[2][1] + b.z*mAR.m[1][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B0 + t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = a.x*mAR.m[1][2] + a.z*mAR.m[1][0] + b.x*mAR.m[2][1] + b.z*mAR.m[0][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B1 + t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = a.x*mAR.m[2][2] + a.z*mAR.m[2][0] + b.x*mAR.m[1][1] + b.y*mAR.m[0][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B2 + t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = a.x*mAR.m[0][1] + a.y*mAR.m[0][0] + b.y*mAR.m[2][2] + b.z*mAR.m[1][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B0 + t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = a.x*mAR.m[1][1] + a.y*mAR.m[1][0] + b.x*mAR.m[2][2] + b.z*mAR.m[0][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B1 + t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = a.x*mAR.m[2][1] + a.y*mAR.m[2][0] + b.x*mAR.m[1][2] + b.y*mAR.m[0][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B2 +#else + t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = a.y*mAR.m[0][2] + a.z*mAR.m[0][1] + b.y*mAR.m[2][0] + b.z*mAR.m[1][0]; if(fabsf(t) > t2) return false; + t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = a.y*mAR.m[1][2] + a.z*mAR.m[1][1] + b.x*mAR.m[2][0] + b.z*mAR.m[0][0]; if(fabsf(t) > t2) return false; + t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = a.y*mAR.m[2][2] + a.z*mAR.m[2][1] + b.x*mAR.m[1][0] + b.y*mAR.m[0][0]; if(fabsf(t) > t2) return false; + t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = a.x*mAR.m[0][2] + a.z*mAR.m[0][0] + b.y*mAR.m[2][1] + b.z*mAR.m[1][1]; if(fabsf(t) > t2) return false; + t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = a.x*mAR.m[1][2] + a.z*mAR.m[1][0] + b.x*mAR.m[2][1] + b.z*mAR.m[0][1]; if(fabsf(t) > t2) return false; + t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = a.x*mAR.m[2][2] + a.z*mAR.m[2][0] + b.x*mAR.m[1][1] + b.y*mAR.m[0][1]; if(fabsf(t) > t2) return false; + t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = a.x*mAR.m[0][1] + a.y*mAR.m[0][0] + b.y*mAR.m[2][2] + b.z*mAR.m[1][2]; if(fabsf(t) > t2) return false; + t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = a.x*mAR.m[1][1] + a.y*mAR.m[1][0] + b.x*mAR.m[2][2] + b.z*mAR.m[0][2]; if(fabsf(t) > t2) return false; + t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = a.x*mAR.m[2][1] + a.y*mAR.m[2][0] + b.x*mAR.m[1][2] + b.y*mAR.m[0][2]; if(fabsf(t) > t2) return false; +#endif + } + return true; +} diff --git a/OpcodeDistrib/OPC_Collider.cpp b/OpcodeDistrib/OPC_Collider.cpp new file mode 100644 index 0000000..4c913a9 --- /dev/null +++ b/OpcodeDistrib/OPC_Collider.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains the abstract class for colliders. + * + * \class Collider + * \author Pierre Terdiman + * \version 1.0 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::Collider() : mFirstContact(false), mTemporalCoherence(false), mContact(false) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::~Collider() +{ +} diff --git a/OpcodeDistrib/OPC_Collider.h b/OpcodeDistrib/OPC_Collider.h new file mode 100644 index 0000000..5788a95 --- /dev/null +++ b/OpcodeDistrib/OPC_Collider.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_COLLIDER_H__ +#define __OPC_COLLIDER_H__ + + class OPCODE_API Collider + { + public: + // Constructor / Destructor + Collider(); + virtual ~Collider(); + + // Collision report + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the last collision status after a collision query. + * \return true if a collision occured + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline bool GetContactStatus() const { return mContact; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Reports all contacts (false) or first contact only (true) + * \param flag [in] true for first contact, false for all contacts + * \see SetTemporalCoherence(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetFirstContact(bool flag) { mFirstContact = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable temporal coherence. + * \param flag [in] true to enable temporal coherence, false to discard it + * \see SetFirstContact(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetTemporalCoherence(bool flag) { mTemporalCoherence = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const char* ValidateSettings() = 0 + { + return "Collider::ValidateSettings: pure virtual function called!"; + } + protected: + // Settings + bool mFirstContact; //!< Report all contacts (false) or only first one (true) + bool mTemporalCoherence; //!< Use temporal coherence or not + + // Collision result + bool mContact; //!< Final contact status after a collision query + }; + +#endif // __OPC_COLLIDER_H__ diff --git a/OpcodeDistrib/OPC_Common.h b/OpcodeDistrib/OPC_Common.h index 9c9426b..20c789a 100644 --- a/OpcodeDistrib/OPC_Common.h +++ b/OpcodeDistrib/OPC_Common.h @@ -52,4 +52,33 @@ uword mExtents[3]; //!< Quantized extents }; + class OPCODE_API CollisionFace + { + public: + //! Constructor + __forceinline CollisionFace() {} + //! Destructor + __forceinline ~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 + __forceinline CollisionFaces() {} + //! Destructor + __forceinline ~CollisionFaces() {} + + __forceinline udword GetNbFaces() const { return GetNbEntries()>>2; } + __forceinline CollisionFace* GetFaces() const { return (CollisionFace*)GetEntries(); } + + __forceinline void Reset() { Container::Reset(); } + + void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); } + }; + #endif //__OPC_COMMON_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_FPU.h b/OpcodeDistrib/OPC_FPU.h deleted file mode 100644 index 8b1df04..0000000 --- a/OpcodeDistrib/OPC_FPU.h +++ /dev/null @@ -1,85 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains FPU related code. - * \file OPC_FPU.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEFPU_H__ -#define __ICEFPU_H__ - - #define SIGN_BITMASK 0x80000000 - - //! Integer representation of a floating-point value. - #define IR(x) ((udword&)(x)) - - //! Absolute integer representation of a floating-point value - #define AIR(x) (IR(x)&0x7fffffff) - - //! Floating-point representation of an integer value. - #define FR(x) ((float&)(x)) - - //! Integer-based comparison of a floating point value. - //! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context. - #define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000) - - #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 - #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 - #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 - #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0 - - #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1 - #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1 - #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1 - #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1 - - #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2 - #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2 - #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 - #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 - - //! A global function to find MAX(a,b,c) using FCOMI/FCMOV - __forceinline float FCMax3(float a, float b, float c) - { - float Res; - _asm fld [a] - _asm fld [b] - _asm fld [c] - FCOMI_ST1 - FCMOVB_ST1 - FCOMI_ST2 - FCMOVB_ST2 - _asm fstp [Res] - _asm fcompp - return Res; - } - - //! A global function to find MIN(a,b,c) using FCOMI/FCMOV - __forceinline float FCMin3(float a, float b, float c) - { - float Res; - _asm fld [a] - _asm fld [b] - _asm fld [c] - FCOMI_ST1 - FCMOVNB_ST1 - FCOMI_ST2 - FCMOVNB_ST2 - _asm fstp [Res] - _asm fcompp - return Res; - } - -#endif // __ICEFPU_H__ diff --git a/OpcodeDistrib/OPC_Matrix3x3.h b/OpcodeDistrib/OPC_Matrix3x3.h deleted file mode 100644 index d40bf02..0000000 --- a/OpcodeDistrib/OPC_Matrix3x3.h +++ /dev/null @@ -1,50 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 3x3 matrices. - * \file OPC_Matrix3x3.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEMATRIX3X3_H__ -#define __ICEMATRIX3X3_H__ - - class OPCODE_API Matrix3x3 - { - public: - //! Empty constructor - __forceinline Matrix3x3() {} - //! Constructor from 9 values - __forceinline 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 - __forceinline Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); } - //! Destructor - __forceinline ~Matrix3x3() {} - - //! Access as rows - __forceinline const Point* operator[](int nRow) const { return (const Point*)&m[nRow][0]; } - //! Access as rows - __forceinline Point* operator[](int nRow) { return (Point*)&m[nRow][0]; } - - public: - float m[3][3]; //!< Array of 9 floats - }; - -#endif // __ICEMATRIX3X3_H__ - diff --git a/OpcodeDistrib/OPC_Matrix4x4.h b/OpcodeDistrib/OPC_Matrix4x4.h deleted file mode 100644 index 83e32dd..0000000 --- a/OpcodeDistrib/OPC_Matrix4x4.h +++ /dev/null @@ -1,109 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 4x4 matrices. - * \file OPC_Matrix4x4.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEMATRIX4X4_H__ -#define __ICEMATRIX4X4_H__ - - class OPCODE_API Matrix4x4 - { - public: - //! Empty constructor. - __forceinline Matrix4x4() {} - //! Constructor from 16 values - __forceinline 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 - __forceinline Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } - //! Destructor. - __forceinline ~Matrix4x4() {} - - //! Gets the translation part of the matrix - __forceinline const Matrix4x4& GetTrans(Point& p) const { p.x = m[3][0]; p.y = m[3][1]; p.z = m[3][2]; return *this; } - //! Sets the translation part of the matrix, from a Point. - __forceinline Matrix4x4& SetTrans(const Point& p) { m[3][0] = p.x; m[3][1] = p.y; m[3][2] = p.z; return *this; } - //! Sets the translation part of the matrix, from floats. - __forceinline Matrix4x4& SetTrans(float tx, float ty, float tz) { m[3][0] = tx; m[3][1] = ty; m[3][2] = tz; return *this; } - - // Row-column access - //! Returns a row. - __forceinline 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. - __forceinline 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. - __forceinline 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. - __forceinline 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; } - - //! Clear the matrix. - __forceinline Matrix4x4& Zero() { ZeroMemory(&m, sizeof(m)); return *this; } - //! Set the identity matrix. - __forceinline Matrix4x4& Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; return *this; } - - // Cast operators - //! Cast a Matrix4x4 to a Matrix3x3. - __forceinline 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]); - } - - //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4; - __forceinline 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]); - } - - public: - - float m[4][4]; //!< Array of 16 floats - }; - - OPCODE_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); - -#endif // __ICEMATRIX4X4_H__ - diff --git a/OpcodeDistrib/OPC_MemoryMacros.h b/OpcodeDistrib/OPC_MemoryMacros.h deleted file mode 100644 index 08b12a8..0000000 --- a/OpcodeDistrib/OPC_MemoryMacros.h +++ /dev/null @@ -1,70 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains all memory macros. - * \file OPC_MemoryMacros.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEMEMORYMACROS_H__ -#define __ICEMEMORYMACROS_H__ - -#undef ZeroMemory -#undef CopyMemory -#undef MoveMemory -#undef FillMemory - -//! A function to clear a buffer. -//! \param addr buffer address -//! \param size buffer length -//! \see FillMemory -//! \see CopyMemory -//! \see MoveMemory -OPCODE_API __forceinline void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } - -//! A function to fill a buffer with a given byte. -//! \param addr buffer address -//! \param size buffer length -//! \param val the byte value -//! \see ZeroMemory -//! \see CopyMemory -//! \see MoveMemory -OPCODE_API __forceinline void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } - -//! A function to copy a buffer. -//! \param addr destination buffer address -//! \param addr source buffer address -//! \param size buffer length -//! \see ZeroMemory -//! \see FillMemory -//! \see MoveMemory -OPCODE_API __forceinline void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); } - -//! A function to move a buffer. -//! \param addr destination buffer address -//! \param addr source buffer address -//! \param size buffer length -//! \see ZeroMemory -//! \see FillMemory -//! \see CopyMemory -OPCODE_API __forceinline void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); } - -#define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)"). -//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE. -#define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class. -#define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array. -#define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release -#define CHECKALLOC(x) if(!x) { SetIceError("Out of memory.", gEC_OutOfMemory); return false;} //!< Standard alloc checking. HANDLE WITH CARE. - -#endif // __ICEMEMORYMACROS_H__ diff --git a/OpcodeDistrib/OPC_Model.cpp b/OpcodeDistrib/OPC_Model.cpp index bdd5f37..7a5a76d 100644 --- a/OpcodeDistrib/OPC_Model.cpp +++ b/OpcodeDistrib/OPC_Model.cpp @@ -130,6 +130,9 @@ using namespace Opcode; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// OPCODE_Model::OPCODE_Model() : mSource(null), mTree(null), mNoLeaf(false), mQuantized(false) { +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + mHull = null; +#endif // __MESHMERIZER_H__ } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -141,11 +144,14 @@ OPCODE_Model::~OPCODE_Model() { DELETESINGLE(mSource); DELETESINGLE(mTree); +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + DELETESINGLE(mHull); +#endif // __MESHMERIZER_H__ } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to build a collision model. + * Builds a collision model. * \param create [in] model creation structure * \return true if success */ @@ -156,13 +162,11 @@ bool OPCODE_Model::Build(const OPCODECREATE& create) if(!create.NbTris || !create.Tris || !create.Verts) return false; // In this lib, we only support complete trees - if(!(create.Rules&SPLIT_COMPLETE)) - { - SetIceError("OPCODE WARNING: supports complete trees only! Use SPLIT_COMPLETE.\n"); - return false; - } + if(!(create.Rules&SPLIT_COMPLETE)) return SetIceError("OPCODE WARNING: supports complete trees only! Use SPLIT_COMPLETE.\n"); - // Check topology + // 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 Triangle* Tris = (const Triangle*)create.Tris; udword NbDegenerate = 0; for(udword i=0;iBuild(&TB)) return false; // 3) Create an optimized tree according to user-settings + // 3-1) Create the correct class mNoLeaf = create.NoLeaf; mQuantized = create.Quantized; @@ -199,11 +206,28 @@ bool OPCODE_Model::Build(const OPCODECREATE& create) } CHECKALLOC(mTree); - // Create optimized tree + // 3-2) Create optimized tree if(!mTree->Build(mSource)) return false; - // Delete generic tree if needed + // 3-3) Delete generic tree if needed if(!create.KeepOriginal) DELETESINGLE(mSource); +#ifdef __MESHMERIZER_H__ + // 4) Convex hull + if(create.CollisionHull) + { + // Create hull + mHull = new CollisionHull; + CHECKALLOC(mHull); + + CONVEXHULLCREATE CHC; + CHC.NbVerts = create.NbVerts; + CHC.Vertices = create.Verts; + CHC.UnifyNormals = true; + CHC.ReduceVertices = true; + CHC.WordFaces = false; + mHull->Compute(CHC); + } +#endif // __MESHMERIZER_H__ return true; } diff --git a/OpcodeDistrib/OPC_Model.h b/OpcodeDistrib/OPC_Model.h index b93a626..2f200cb 100644 --- a/OpcodeDistrib/OPC_Model.h +++ b/OpcodeDistrib/OPC_Model.h @@ -33,6 +33,9 @@ Rules = SPLIT_COMPLETE | SPLIT_LARGESTAXIS; NoLeaf = true; Quantized = true; +#ifdef __MESHMERIZER_H__ + CollisionHull = false; +#endif // __MESHMERIZER_H__ KeepOriginal = false; } @@ -43,6 +46,9 @@ 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) }; @@ -55,7 +61,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to build a collision model. + * Builds a collision model. * \param create [in] model creation structure * \return true if success */ @@ -78,9 +84,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// __forceinline const AABBTree* GetSourceTree() const { return mSource; } +#ifdef __MESHMERIZER_H__ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to check whether the tree has leaf nodes or not. + * A method to access the collision hull. + * \return the collision hull if it exists + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline const CollisionHull* GetHull() const { return mHull; } +#endif // __MESHMERIZER_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree has leaf nodes or not. * \return true if the tree has leaf nodes (normal tree), else false (optimized tree) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -88,7 +104,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to check whether the tree is quantized or not. + * Checks whether the tree is quantized or not. * \return true if the tree is quantized */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -96,7 +112,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to get the number of nodes in the tree. + * 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 */ @@ -105,7 +121,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * A method to get the number of bytes used by the tree. + * Gets the number of bytes used by the tree. * \return amount of bytes used */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -113,6 +129,9 @@ 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 }; diff --git a/OpcodeDistrib/OPC_OptimizedTree.cpp b/OpcodeDistrib/OPC_OptimizedTree.cpp index 91f5933..eabe9a1 100644 --- a/OpcodeDistrib/OPC_OptimizedTree.cpp +++ b/OpcodeDistrib/OPC_OptimizedTree.cpp @@ -245,6 +245,7 @@ bool AABBCollisionTree::Build(AABBTree* tree) 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; } @@ -297,6 +298,7 @@ bool AABBNoLeafTree::Build(AABBTree* tree) 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))); #endif + return true; } @@ -313,6 +315,7 @@ 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) #define FIND_MAX_VALUES \ /* Get max values */ \ Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ @@ -542,5 +545,6 @@ bool AABBQuantizedNoLeafTree::Build(AABBTree* tree) Log("Original tree: %d nodes, depth %d\n", NbNodes, tree->ComputeDepth()); Log("AABB quantized no-leaf tree: %d nodes, %d bytes - Alignment: %d\n", mNbNodes, GetUsedBytes(), Alignment(udword(mNodes))); #endif + return true; } diff --git a/OpcodeDistrib/OPC_Point.h b/OpcodeDistrib/OPC_Point.h deleted file mode 100644 index d8bac87..0000000 --- a/OpcodeDistrib/OPC_Point.h +++ /dev/null @@ -1,127 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * OPCODE - Optimized Collision Detection - * Copyright (C) 2001 Pierre Terdiman - * Homepage: http://www.codercorner.com/Opcode.htm - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Contains code for 3D vectors. - * \file OPC_Point.h - * \author Pierre Terdiman - * \date April, 4, 2000 - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Include Guard -#ifndef __ICEPOINT_H__ -#define __ICEPOINT_H__ - - class Plane; - class Matrix3x3; - class Matrix4x4; - - class OPCODE_API Point - { - public: - - //! Empty constructor - __forceinline Point() {} - //! Constructor from floats - __forceinline Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} - //! Constructor from array - __forceinline Point(float f[3]) : x(f[0]), y(f[1]), z(f[2]) {} - //! Copy constructor - __forceinline Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} - //! Destructor - __forceinline ~Point() {} - - //! Returns MIN(x, y, z); - __forceinline float Min() const { return MIN(x, MIN(y, z)); } - //! Returns MAX(x, y, z); - __forceinline float Max() const { return MAX(x, MAX(y, z)); } - //! TO BE DOCUMENTED - __forceinline Point& Min(const Point& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; } - //! TO BE DOCUMENTED - __forceinline Point& Max(const Point& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; } - - //! Computes square magnitude - __forceinline float SquareMagnitude() const { return x*x + y*y + z*z; } - //! Computes magnitude - __forceinline float Magnitude() const { return sqrtf(x*x + y*y + z*z); } - - //! Return largest axis - __forceinline udword LargestAxis() const - { - const float* Vals = &x; - udword m = 0; - if(Vals[1] > Vals[m]) m = 1; - if(Vals[2] > Vals[m]) m = 2; - return m; - } - - // Arithmetic operators - //! Operator for Point Negate = - Point - __forceinline Point operator-() const { return Point(-x, -y, -z); } - - //! Operator for Point Plus = Point + Point. - __forceinline Point operator+(const Point& p) const { return Point(x + p.x, y + p.y, z + p.z); } - //! Operator for Point Minus = Point - Point. - __forceinline Point operator-(const Point& p) const { return Point(x - p.x, y - p.y, z - p.z); } - //! Operator for Point Scale = Point * float. - __forceinline Point operator*(float s) const { return Point(x * s, y * s, z * s ); } - //! Operator for Point Scale = float * Point. - friend Point operator*(float s, const Point& p) { return Point(s * p.x, s * p.y, s * p.z); } - //! Operator for Point Scale = Point / float. - __forceinline Point operator/(float s) const { s = 1.0f / s; return Point(x * s, y * s, z * s); } - - //! Operator for float DotProd = Point | Point. - __forceinline float operator|(const Point& p) const { return x*p.x + y*p.y + z*p.z; } - //! Operator for Point VecProd = Point ^ Point. - __forceinline Point operator^(const Point& p) const - { - return Point( - y * p.z - z * p.y, - z * p.x - x * p.z, - x * p.y - y * p.x ); - } - - //! Operator for Point += Point. - __forceinline Point& operator+=(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } - //! Operator for Point += float. - __forceinline Point& operator+=(float s) { x += s; y += s; z += s; return *this; } - - //! Operator for Point -= Point. - __forceinline Point& operator-=(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } - //! Operator for Point -= float. - __forceinline Point& operator-=(float s) { x -= s; y -= s; z -= s; return *this; } - //! Operator for Point *= float. - __forceinline Point& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } - //! Operator for Point /= float. - __forceinline Point& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; } - - // Arithmetic operators - //! Operator for Point Mul = Point * Matrix3x3. - Point operator*(const Matrix3x3& mat) const; - //! Operator for Point Mul = Point * Matrix4x4. - Point operator*(const Matrix4x4& mat) const; - //! Operator for Point *= Matrix3x3. - Point& operator*=(const Matrix3x3& mat); - //! Operator for Point *= Matrix4x4. - Point& operator*=(const Matrix4x4& mat); - - //! Access as array - __forceinline operator const float*() const { return &x; } - //! Access as array - __forceinline operator float*() { return &x; } - - public: - float x; //!< x coordinate - float y; //!< y coordinate - float z; //!< z coordinate - }; - -#endif //__ICEPOINT_H__ diff --git a/OpcodeDistrib/OPC_RayAABBOverlap.h b/OpcodeDistrib/OPC_RayAABBOverlap.h new file mode 100644 index 0000000..1c19913 --- /dev/null +++ b/OpcodeDistrib/OPC_RayAABBOverlap.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-AABB intersection. + * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 + * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) + * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) + * + * Hence this version is faster as well as more robust than the original one. + * + * Should work provided: + * 1) the integer representation of 0.0f is 0x00000000 + * 2) the sign bit of the float is the most significant one + * + * Report bugs: p.terdiman@codercorner.com + * + * \param center [in] box center + * \param extents [in] box extents + * \param coord [out] impact coordinates + * \return true if ray intersects AABB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +__forceinline bool AABBRayCollider::RayAABBOverlap(const Point& center, const Point& extents, Point& coord) +{ + // Stats + mNbRayBVTests++; + + // We need the box in its (Min, Max) form here. Pity, the (Center, Extents) one is + // used for tree-tree collision... Nonetheless by lazy-evaluating Min & Max we may + // save some work. (not done here) + Point MinB = center - extents; + Point MaxB = center + extents; + + Point MaxT; + MaxT.x=MaxT.y=MaxT.z=-1.0f; + BOOL Inside = TRUE; + + // Find candidate planes. + for(udword i=0;i<3;i++) + { + if(mOrigin[i] < MinB[i]) + { + coord[i] = MinB[i]; + Inside = FALSE; + + // Calculate T distances to candidate planes + MaxT[i] = (MinB[i] - mOrigin[i]) * mOneOverDir[i]; + } + else if(mOrigin[i] > MaxB[i]) + { + coord[i] = MaxB[i]; + Inside = FALSE; + + // Calculate T distances to candidate planes + MaxT[i] = (MaxB[i] - mOrigin[i]) * mOneOverDir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = mOrigin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + udword WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + if(IR(MaxT[WhichPlane])&0x80000000) return false; + + for(i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = mOrigin[i] + MaxT[WhichPlane] * mDir[i]; +#ifdef OPC_RAYAABB_EPSILON + if(coord[i] < MinB[i] - OPC_RAYAABB_EPSILON || coord[i] > MaxB[i] + OPC_RAYAABB_EPSILON) return false; +#else + if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false; +#endif + } + } + return true; // ray hits box +} diff --git a/OpcodeDistrib/OPC_RayCollider.cpp b/OpcodeDistrib/OPC_RayCollider.cpp new file mode 100644 index 0000000..5d56b1b --- /dev/null +++ b/OpcodeDistrib/OPC_RayCollider.cpp @@ -0,0 +1,597 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an AABB ray collider. + * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision. + * + * HIGHER DISTANCE BOUND: + * + * If P0 and P1 are two 3D points, let's define: + * - d = distance between P0 and P1 + * - Origin = P0 + * - Direction = (P1 - P0) / d = normalized direction vector + * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction + * - t = 0 --> P = P0 + * - t = d --> P = P1 + * + * Then we can define a general "ray" as: + * + * struct Ray + * { + * Point Origin; + * Point Direction; + * }; + * + * But it actually maps three different things: + * - a segment, when 0 <= t <= d + * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d + * - a line, when -infinity < t < +infinity + * + * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity. + * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin. + * + * In short, the lower bound is always 0, and you can setup the higher bound "d" with AABBRayCollider::SetMaxDist(). + * + * Query |segment |half-line |line + * --------|-------------------|---------------|---------------- + * Usages |-shadow feelers |-raytracing |- + * |-sweep tests |-in/out tests | + * + * For true segment queries using (P0, P1) instead of (Origin, Dir), the ray-aabb overlap test *could* be replaced with + * a more efficient one. Here I'm thinking about the Volition one by Tim Schröder, which appears to be a bit faster in + * some cases, but sometimes a bit slower. YMMV. Note that Tim's code in GDMag can easily be optimized. + * + * FIRST CONTACT: + * + * - You can setup "first contact" mode or "all contacts" mode with AABBRayCollider::SetFirstContact(). + * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where + * you want to know whether the path to the light is free or not (a boolean answer is enough). + * - In "all contacts" mode we return all faces hit by the ray. + * + * TEMPORAL COHERENCE: + * + * - You can enable or disable temporal coherence with AABBRayCollider::SetTemporalCoherence(). + * - It currently only works in "first contact" mode. + * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries + * start by colliding the ray against the cached triangle. If they still colliden we return immediately. + * + * CLOSEST HIT: + * + * - You can enable or disable "closest hit" with AABBRayCollider::SetClosestHit(). + * - It currently only works in "all contact" mode. + * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported. + * + * BACKFACE CULLING: + * + * - You can enable or disable backface culling with AABBRayCollider::SetCulling(). + * - If culling is enabled, ray will not hit back faces (only front faces). + * + * + * + * \class AABBRayCollider + * \author Pierre Terdiman + * \version 1.0 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_RayAABBOverlap.h" +#include "OPC_RayTriOverlap.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBRayCollider::AABBRayCollider() + : mNbRayBVTests (0), + mNbRayPrimTests (0), + mNbIntersections (0), + mClosestHit (false), + mCulling (true), + mUserData (0), + mObjCallback (null), + mStabbedFaces (null), + mMaxDist (MAX_FLOAT), + mMaxDist2 (MAX_FLOAT) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBRayCollider::~AABBRayCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 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* AABBRayCollider::ValidateSettings() +{ + if(!mObjCallback) return "Object callback must be defined! Call: SetCallbackObj()."; + if(!mStabbedFaces) return "Destination array must be defined! Call: SetDestination()."; + if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; + if(mTemporalCoherence && !mFirstContact) return "Temporal coherence only works with ""First contact"" mode!"; + if(mClosestHit && mFirstContact) return "Closest hit doesn't work with ""First contact"" mode!"; + return null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param worldray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBRayCollider::Collide(const Ray& worldray, OPCODE_Model* model, const Matrix4x4& world, udword* cache) +{ + // Checkings + if(!model) return false; + + // Simple double-dispatch + if(!model->HasLeafNodes()) + { + if(model->IsQuantized()) + { + const AABBQuantizedNoLeafTree* T = (const AABBQuantizedNoLeafTree*)model->GetTree(); + return Collide(worldray, T, world, cache); + } + else + { + const AABBNoLeafTree* T = (const AABBNoLeafTree*)model->GetTree(); + return Collide(worldray, T, world, cache); + } + } + else + { + if(model->IsQuantized()) + { + const AABBQuantizedTree* T = (const AABBQuantizedTree*)model->GetTree(); + return Collide(worldray, T, world, cache); + } + else + { + const AABBCollisionTree* T = (const AABBCollisionTree*)model->GetTree(); + return Collide(worldray, T, world, cache); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a stabbing query : + * - reset stats & contact status + * - compute ray in local space + * + * \param worldray [in] stabbing ray in world space + * \param world [in] object's world matrix + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBRayCollider::InitQuery(const Ray& worldray, const Matrix4x4& world) +{ + // Reset stats & contact status + mContact = false; + mNbRayBVTests = 0; + mNbRayPrimTests = 0; + mNbIntersections = 0; + if(mStabbedFaces) mStabbedFaces->Reset(); + + // Compute ray in local space + Matrix3x3 InvWorld = world; + mDir = InvWorld * worldray.mDir; + + Matrix4x4 World; + InvertPRMatrix(World, world); + mOrigin = worldray.mOrig * World; + + // Precompute 1.0f / dir + if(IR(mDir.x)) mOneOverDir.x = 1.0f / mDir.x; + else mOneOverDir.x = 0.0f; + if(IR(mDir.y)) mOneOverDir.y = 1.0f / mDir.y; + else mOneOverDir.y = 0.0f; + if(IR(mDir.z)) mOneOverDir.z = 1.0f / mDir.z; + else mOneOverDir.z = 0.0f; + + // Precompute mMaxDist2 + mMaxDist2 = mMaxDist * mMaxDist; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A method to take advantage of temporal coherence. + * \param faceid [in] index of previously stabbed triangle + * \warning only works for "First Contact" mode + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBRayCollider::CheckTemporalCoherence(udword* faceid) +{ + // mObjCallback && mStabbedFaces have already been checked. + + // Test previously colliding primitives first + if(mTemporalCoherence && mFirstContact && faceid && *faceid!=INVALID_ID) + { + // Request vertices from the app + VertexPointers VP; (mObjCallback)(*faceid, VP, mUserData); + + // Perform ray-cached tri overlap + static CollisionFace CF; + CF.mFaceID = *faceid; + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2], CF.mDistance, CF.mU, CF.mV)) + { + // 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(CF.mDistance>0.0f && CF.mDistanceAddFace(CF); + return true; + } + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for normal trees. + * \param worldray [in] stabbing ray in world space + * \param tree [in] object's AABB tree + * \param world [in] object's world matrix + * \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 AABBRayCollider::Collide(const Ray& worldray, const AABBCollisionTree* tree, const Matrix4x4& world, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mStabbedFaces) return false; + + // Init collision query + InitQuery(worldray, world); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform stabbing query + _Stab(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mStabbedFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for no-leaf trees. + * \param worldray [in] stabbing ray in world space + * \param tree [in] object's AABB tree + * \param world [in] object's world matrix + * \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 AABBRayCollider::Collide(const Ray& worldray, const AABBNoLeafTree* tree, const Matrix4x4& world, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mStabbedFaces) return false; + + // Init collision query + InitQuery(worldray, world); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform stabbing query + _Stab(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mStabbedFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for quantized trees. + * \param worldray [in] stabbing ray in world space + * \param tree [in] object's AABB tree + * \param world [in] object's world matrix + * \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 AABBRayCollider::Collide(const Ray& worldray, const AABBQuantizedTree* tree, const Matrix4x4& world, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mStabbedFaces) return false; + + // Init collision query + InitQuery(worldray, world); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff = tree->mCenterCoeff; + mExtentsCoeff = tree->mExtentsCoeff; + + // Perform stabbing query + _Stab(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mStabbedFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for quantized no-leaf trees. + * \param worldray [in] stabbing ray in world space + * \param tree [in] object's AABB tree + * \param world [in] object's world matrix + * \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 AABBRayCollider::Collide(const Ray& worldray, const AABBQuantizedNoLeafTree* tree, const Matrix4x4& world, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mStabbedFaces) return false; + + // Init collision query + InitQuery(worldray, world); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff = tree->mCenterCoeff; + mExtentsCoeff = tree->mExtentsCoeff; + + // Perform stabbing query + _Stab(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mStabbedFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +#define STAB_PRIM(prim) \ + /* Request vertices from the app */ \ + VertexPointers VP; (mObjCallback)(prim, VP, mUserData); \ + \ + /* Perform ray-tri overlap test and return */ \ + static CollisionFace CF; \ + CF.mFaceID = prim; \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2], CF.mDistance, CF.mU, CF.mV)) \ + { \ + /* 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(CF.mDistance>0.0f) \ + { \ + mNbIntersections++; \ + if(CF.mDistanceAddFace(CF); \ + } \ + else \ + { \ + CollisionFace* Current = mStabbedFaces->GetFaces(); \ + if(!Current) mStabbedFaces->AddFace(CF); \ + else if(CF.mDistancemDistance) *Current = CF; \ + } \ + } \ + } \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBRayCollider::_Stab(const AABBCollisionNode* node) +{ + // Perform Ray-AABB overlap test + Point Coords; + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, Coords)) return; + + // Test distance to box, so that we can stop the query earlier when the box is out of range. + // Avoid the test when distance is max + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + float Dist2 = Coords.SquareDistance(mOrigin); + if(Dist2 > mMaxDist2) return; + } + + if(node->IsLeaf()) + { + STAB_PRIM(node->GetPrimitive()) + } + else + { + _Stab(node->GetPos()); + if(mFirstContact && mContact) return; + _Stab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBRayCollider::_Stab(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 Ray-AABB overlap test + Point Coords; + if(!RayAABBOverlap(Center, Extents, Coords)) return; + + // Test distance to box, so that we can stop the query earlier when the box is out of range. + // Avoid the test when distance is max + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + float Dist2 = Coords.SquareDistance(mOrigin); + if(Dist2 > mMaxDist2) return; + } + + if(node->IsLeaf()) + { + STAB_PRIM(node->GetPrimitive()) + } + else + { + _Stab(node->GetPos()); + if(mFirstContact && mContact) return; + _Stab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBRayCollider::_Stab(const AABBNoLeafNode* node) +{ + // Perform Ray-AABB overlap test + Point Coords; + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, Coords)) return; + + // Test distance to box, so that we can stop the query earlier when the box is out of range. + // Avoid the test when distance is max + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + float Dist2 = Coords.SquareDistance(mOrigin); + if(Dist2 > mMaxDist2) return; + } + + if(node->HasLeaf()) + { + STAB_PRIM(node->GetPrimitive()) + } + else _Stab(node->GetPos()); + + if(mFirstContact && mContact) return; + + if(node->HasLeaf2()) + { + STAB_PRIM(node->GetPrimitive2()) + } + else _Stab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBRayCollider::_Stab(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 Ray-AABB overlap test + Point Coords; + if(!RayAABBOverlap(Center, Extents, Coords)) return; + + // Test distance to box, so that we can stop the query earlier when the box is out of range. + // Avoid the test when distance is max + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + float Dist2 = Coords.SquareDistance(mOrigin); + if(Dist2 > mMaxDist2) return; + } + + if(node->HasLeaf()) + { + STAB_PRIM(node->GetPrimitive()) + } + else _Stab(node->GetPos()); + + if(mFirstContact && mContact) return; + + if(node->HasLeaf2()) + { + STAB_PRIM(node->GetPrimitive2()) + } + else _Stab(node->GetNeg()); +} diff --git a/OpcodeDistrib/OPC_RayCollider.h b/OpcodeDistrib/OPC_RayCollider.h new file mode 100644 index 0000000..be697da --- /dev/null +++ b/OpcodeDistrib/OPC_RayCollider.h @@ -0,0 +1,195 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_RAYCOLLIDER_H__ +#define __OPC_RAYCOLLIDER_H__ + + class OPCODE_API AABBRayCollider : public Collider + { + public: + // Constructor / Destructor + AABBRayCollider(); + virtual ~AABBRayCollider(); + // Generic collision query + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param worldray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(const Ray& worldray, OPCODE_Model* model, const Matrix4x4& world, udword* cache=null); + + // Collision queries + bool Collide(const Ray& worldray, const AABBCollisionTree* tree, const Matrix4x4& world, udword* cache=null); + bool Collide(const Ray& worldray, const AABBNoLeafTree* tree, const Matrix4x4& world, udword* cache=null); + bool Collide(const Ray& worldray, const AABBQuantizedTree* tree, const Matrix4x4& world, udword* cache=null); + bool Collide(const Ray& worldray, const AABBQuantizedNoLeafTree* tree, const Matrix4x4& world, udword* cache=null); + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * 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 SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetClosestHit(bool flag) { mClosestHit = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: enable or disable backface culling. + * \param flag [in] true to enable backface culling + * \see SetClosestHit(bool flag) + * \see SetMaxDist(float maxdist) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetCulling(bool flag) { mCulling = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the higher distance bound. + * \param maxdist [in] higher distance bound. + * \see SetClosestHit(bool flag) + * \see SetCulling(bool flag) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetMaxDist(float maxdist) { mMaxDist = maxdist; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * 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) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-BV overlap tests after a collision query. + * \see GetNbRayPrimTests() + * \see GetNbIntersections() + * \return the number of Ray-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline udword GetNbRayBVTests() const { return mNbRayBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-Triangle overlap tests after a collision query. + * \see GetNbRayBVTests() + * \see GetNbIntersections() + * \return the number of Ray-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline udword GetNbRayPrimTests() const { return mNbRayPrimTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of intersection found after a collision query. Can be used for in/out tests. + * \see GetNbRayBVTests() + * \see GetNbRayPrimTests() + * \return the number of valid intersections during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline udword GetNbIntersections() const { return mNbIntersections; } + + // Callback control + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: a method to setup user-data assigned to object callback. + * \param data [in] user-defined data + * \return Self-Reference + * \see SetCallbackObj(OPC_CALLBACK callback) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline AABBRayCollider& SetUserData(udword data) { mUserData = data; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: a method to setup object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \return Self-Reference + * \see SetUserData(udword data) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline AABBRayCollider& SetCallbackObj(OPC_CALLBACK callback) { mObjCallback = callback; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * 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(); + + private: + // Ray in local space + Point mOrigin; //!< Ray origin + Point mDir; //!< Ray direction (normalized) + Point mOneOverDir; //!< Precomputed 1.0 / dir + // Stabbed faces + CollisionFaces* mStabbedFaces; //!< List of stabbed faces + // User callback + udword mUserData; //!< User-defined data sent to callback + OPC_CALLBACK mObjCallback; //!< Object callback + // Stats + udword mNbRayBVTests; //!< Number of Ray-BV tests + udword mNbRayPrimTests; //!< Number of Ray-Primitive tests + udword mNbIntersections; //!< Number of valid intersections + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Settings + float mMaxDist; //!< Valid segment on the ray + float mMaxDist2; //!< Precomputed mMaxDist^2 + bool mClosestHit; //!< Report closest hit only + 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); + // Overlap tests + bool RayAABBOverlap(const Point& center, const Point& extents, Point& coord); + bool RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2, float& t, float& u, float& v); + // Init methods + void InitQuery(const Ray& worldray, const Matrix4x4& world); + bool CheckTemporalCoherence(udword* faceid); + }; + +#endif // __OPC_RAYCOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_RayTriOverlap.h b/OpcodeDistrib/OPC_RayTriOverlap.h new file mode 100644 index 0000000..6329ad9 --- /dev/null +++ b/OpcodeDistrib/OPC_RayTriOverlap.h @@ -0,0 +1,84 @@ +#define LOCAL_EPSILON 0.000001f + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-triangle intersection test. + * From Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection" + * + * \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 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +__forceinline bool AABBRayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2, float& t, float& u, float& v) +{ + // Stats + mNbRayPrimTests++; + + // Find vectors for two edges sharing vert0 + Point edge1 = vert1 - vert0; + Point edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + Point pvec = mDir^edge2; + + // If determinant is near zero, ray lies in plane of triangle + float det = edge1|pvec; + + if(mCulling) + { + if(det 0. So we can use integer cmp. + + // Calculate distance from vert0 to ray origin + Point tvec = mOrigin - vert0; + + // Calculate U parameter and test bounds + u = tvec|pvec; +// if(IR(u)&0x80000000 || u>det) return false; + if(IR(u)&0x80000000 || IR(u)>IR(det)) return false; + + // Prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + v = mDir|qvec; + if(IR(v)&0x80000000 || u+v>det) return false; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2|qvec; + float inv_det = 1.0f / det; + t *= inv_det; + u *= inv_det; + v *= inv_det; + } + else + { + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) return false; + if(IR(u)&0x80000000 || IR(u)>IEEE_1_0) return false; + + // prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + v = (mDir|qvec) * inv_det; + if(IR(v)&0x80000000 || u+v>1.0f) return false; + + // Calculate t, ray intersects triangle + t = (edge2|qvec) * inv_det; + } + return true; +} diff --git a/OpcodeDistrib/OPC_Triangle.h b/OpcodeDistrib/OPC_Settings.h similarity index 64% rename from OpcodeDistrib/OPC_Triangle.h rename to OpcodeDistrib/OPC_Settings.h index 47884c2..51b8d2f 100644 --- a/OpcodeDistrib/OPC_Triangle.h +++ b/OpcodeDistrib/OPC_Settings.h @@ -8,34 +8,31 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Contains a triangle class. - * \file OPC_Triangle.h + * Contains compilation flags. + * \file OPC_Settings.h * \author Pierre Terdiman - * \date January, 17, 2000 + * \date May, 12, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICETRIANGLE_H__ -#define __ICETRIANGLE_H__ - - // An indexed triangle class. - class OPCODE_API Triangle - { - public: - //! Constructor - Triangle() {} - //! Constructor - Triangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; } - //! Destructor - ~Triangle() {} - //! Vertex-references - udword mVRef[3]; - - // Methods - void Center(const Point* verts, Point& center) const; - bool IsDegenerate() const; - }; - -#endif // __ICETRIANGLE_H__ +#ifndef __OPC_SETTINGS_H__ +#define __OPC_SETTINGS_H__ + + //! Use CPU comparisons (comment that line to use standard FPU compares) + #define OPC_CPU_COMPARE + + //! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++) + #define OPC_USE_FCOMI + + //! Use epsilon value in tri-tri overlap test + #define OPC_TRITRI_EPSILON_TEST + + //! Use epsilon value in ray-AABB overlap test +// #define OPC_RAYAABB_EPSILON 0.00001f + + //! Use tree-coherence or not + #define OPC_USE_TREE_COHERENCE + +#endif //__OPC_SETTINGS_H__ \ No newline at end of file diff --git a/OpcodeDistrib/OPC_SphereAABBOverlap.h b/OpcodeDistrib/OPC_SphereAABBOverlap.h new file mode 100644 index 0000000..ea26d6d --- /dev/null +++ b/OpcodeDistrib/OPC_SphereAABBOverlap.h @@ -0,0 +1,64 @@ + +__forceinline bool AABBSphereCollider::SphereAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbSphereBVTests++; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(udword i=0;i<3;i++) + { + float tmp = mCenter[i] - center[i]; + float s = tmp + extents[i]; + + if(s<0.0f) + { + d += s*s; + } + else + { + s = tmp - extents[i]; + + if(s>0.0f) + { + d += s*s; + } + } + } + +#ifdef OLDIES +// Point Min = center - extents; +// Point Max = center + extents; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(udword i=0;i<3;i++) + { +float Min = center[i] - extents[i]; + +// if(mCenter[i]Max[i]) + if(mCenter[i]>Max) + { + float s = mCenter[i] - Max; + d += s*s; + } + } + } +#endif + return d <= mRadius2; +} diff --git a/OpcodeDistrib/OPC_SphereCollider.cpp b/OpcodeDistrib/OPC_SphereCollider.cpp new file mode 100644 index 0000000..1df9c91 --- /dev/null +++ b/OpcodeDistrib/OPC_SphereCollider.cpp @@ -0,0 +1,464 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an AABB sphere collider. + * This class performs a collision test between a sphere and an AABB tree. You can use this to do a standard player vs world collision, + * in a Nettle/Telemachos way. It doesn't suffer from all reported bugs in those two classic codes - the "new" one by Paul Nettle is a + * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of + * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever). + * + * \class AABBSphereCollider + * \author Pierre Terdiman + * \version 1.0 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_SphereAABBOverlap.h" +#include "OPC_SphereTriOverlap.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBSphereCollider::AABBSphereCollider() + : mNbSphereBVTests (0), + mNbSpherePrimTests (0), + mUserData (0), + mObjCallback (null), + mFaces (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBSphereCollider::~AABBSphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 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* AABBSphereCollider::ValidateSettings() +{ + if(!mObjCallback) return "Object callback must be defined! Call: SetCallbackObj()."; + if(!mFaces) return "Destination array must be defined! Call: SetDestination()."; + if(mTemporalCoherence && !mFirstContact) return "Temporal coherence only works with ""First contact"" mode!"; + + return null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::Collide(const Sphere& sphere, OPCODE_Model* model, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache) +{ + // Checkings + if(!model) return false; + + // Simple double-dispatch + if(!model->HasLeafNodes()) + { + if(model->IsQuantized()) + { + const AABBQuantizedNoLeafTree* T = (const AABBQuantizedNoLeafTree*)model->GetTree(); + return Collide(sphere, T, worlds, worldm, cache); + } + else + { + const AABBNoLeafTree* T = (const AABBNoLeafTree*)model->GetTree(); + return Collide(sphere, T, worlds, worldm, cache); + } + } + else + { + if(model->IsQuantized()) + { + const AABBQuantizedTree* T = (const AABBQuantizedTree*)model->GetTree(); + return Collide(sphere, T, worlds, worldm, cache); + } + else + { + const AABBCollisionTree* T = (const AABBCollisionTree*)model->GetTree(); + return Collide(sphere, T, worlds, worldm, cache); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * + * \param sphere [in] sphere in local space + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBSphereCollider::InitQuery(const Sphere& sphere, const Matrix4x4& worlds, const Matrix4x4& worldm) +{ + // Reset stats & contact status + mContact = false; + mNbSphereBVTests = 0; + mNbSpherePrimTests = 0; + if(mFaces) mFaces->Reset(); + + // Compute sphere in model space + + // Setup matrices + Matrix4x4 InvWorld1; + InvertPRMatrix(InvWorld1, worldm); + Matrix4x4 WorldStoM = worlds * InvWorld1; + + mCenter = sphere.mCenter * WorldStoM; + mRadius2 = sphere.mRadius * sphere.mRadius; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A method to take advantage of temporal coherence. + * \param faceid [in] index of previously collided triangle + * \warning only works for "First Contact" mode + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::CheckTemporalCoherence(udword* faceid) +{ + // mObjCallback && mFaces have already been checked. + + // Test previously colliding primitives first + if(mTemporalCoherence && mFirstContact && faceid && *faceid!=INVALID_ID) + { + // Request vertices from the app + VertexPointers VP; (mObjCallback)(*faceid, VP, mUserData); + + // Perform sphere-cached tri overlap + static CollisionFace CF; + CF.mFaceID = *faceid; + if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2], CF.mDistance, CF.mU, CF.mV)) + { + // Set contact status + mContact = true; + + mFaces->AddFace(CF); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for normal trees. + * \param sphere [in] collision sphere in local space + * \param tree [in] model's AABB tree + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::Collide(const Sphere& sphere, const AABBCollisionTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mFaces) return false; + + // Init collision query + InitQuery(sphere, worlds, worldm); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for no-leaf trees. + * \param sphere [in] collision sphere in local space + * \param tree [in] model's AABB tree + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::Collide(const Sphere& sphere, const AABBNoLeafTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mFaces) return false; + + // Init collision query + InitQuery(sphere, worlds, worldm); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized trees. + * \param sphere [in] collision sphere in local space + * \param tree [in] model's AABB tree + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::Collide(const Sphere& sphere, const AABBQuantizedTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mFaces) return false; + + // Init collision query + InitQuery(sphere, worlds, worldm); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff = tree->mCenterCoeff; + mExtentsCoeff = tree->mExtentsCoeff; + + // Perform collision query + _Collide(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized no-leaf trees. + * \param sphere [in] collision sphere in local space + * \param tree [in] model's AABB tree + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBSphereCollider::Collide(const Sphere& sphere, const AABBQuantizedNoLeafTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache) +{ + // Checkings + if(!tree || !mObjCallback || !mFaces) return false; + + // Init collision query + InitQuery(sphere, worlds, worldm); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff = tree->mCenterCoeff; + mExtentsCoeff = tree->mExtentsCoeff; + + // Perform collision query + _Collide(tree->GetNodes()); + + // Update cache if needed + if(cache && mContact) + { + CollisionFace* Current = mFaces->GetFaces(); + if(Current) *cache = Current->mFaceID; + else *cache = INVALID_ID; + } + return true; +} + +#define SPHERE_PRIM(prim) \ + /* Request vertices from the app */ \ + VertexPointers VP; (mObjCallback)(prim, VP, mUserData); \ + \ + /* Perform sphere-tri overlap test and return */ \ + static CollisionFace CF; \ + CF.mFaceID = prim; \ + if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2], CF.mDistance, CF.mU, CF.mV)) \ + { \ + /* Set contact status */ \ + mContact = true; \ + mFaces->AddFace(CF); \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBSphereCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive()) + } + else + { + _Collide(node->GetPos()); + if(mFirstContact && mContact) return; + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBSphereCollider::_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 Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive()) + } + else + { + _Collide(node->GetPos()); + if(mFirstContact && mContact) return; + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBSphereCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->HasLeaf()) + { + SPHERE_PRIM(node->GetPrimitive()) + } + else _Collide(node->GetPos()); + + if(mFirstContact && mContact) return; + + if(node->HasLeaf2()) + { + SPHERE_PRIM(node->GetPrimitive2()) + } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBSphereCollider::_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 Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + if(node->HasLeaf()) + { + SPHERE_PRIM(node->GetPrimitive()) + } + else _Collide(node->GetPos()); + + if(mFirstContact && mContact) return; + + if(node->HasLeaf2()) + { + SPHERE_PRIM(node->GetPrimitive2()) + } + else _Collide(node->GetNeg()); +} diff --git a/OpcodeDistrib/OPC_SphereCollider.h b/OpcodeDistrib/OPC_SphereCollider.h new file mode 100644 index 0000000..6033b35 --- /dev/null +++ b/OpcodeDistrib/OPC_SphereCollider.h @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SPHERECOLLIDER_H__ +#define __OPC_SPHERECOLLIDER_H__ + + class OPCODE_API AABBSphereCollider : public Collider + { + public: + // Constructor / Destructor + AABBSphereCollider(); + virtual ~AABBSphereCollider(); + // Generic collision query + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix + * \param worldm [in] model's world matrix + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(const Sphere& sphere, OPCODE_Model* model, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache=null); + + // Collision queries + bool Collide(const Sphere& sphere, const AABBCollisionTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache=null); + bool Collide(const Sphere& sphere, const AABBNoLeafTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache=null); + bool Collide(const Sphere& sphere, const AABBQuantizedTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache=null); + bool Collide(const Sphere& sphere, const AABBQuantizedNoLeafTree* tree, const Matrix4x4& worlds, const Matrix4x4& worldm, udword* cache=null); + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the destination array for stabbed faces. + * \param cf [in] destination array, filled during queries + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline void SetDestination(CollisionFaces* cf) { mFaces = cf; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Sphere-BV overlap tests after a collision query. + * \see GetNbSpherePrimTests() + * \return the number of Sphere-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline udword GetNbSphereBVTests() const { return mNbSphereBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Sphere-Triangle overlap tests after a collision query. + * \see GetNbSphereBVTests() + * \return the number of Sphere-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline udword GetNbSpherePrimTests() const { return mNbSpherePrimTests; } + + // Callback control + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: a method to setup user-data assigned to object callback. + * \param data [in] user-defined data + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline AABBSphereCollider& SetUserData(udword data) { mUserData = data; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: a method to setup object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + __forceinline AABBSphereCollider& SetCallbackObj(OPC_CALLBACK callback) { mObjCallback = callback; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * 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(); + + private: + // Sphere in local space + Point mCenter; //!< Sphere center + float mRadius2; //!< Sphere radius squared + // Colliding faces + CollisionFaces* mFaces; //!< List of collided faces + // User callback + udword mUserData; //!< User-defined data sent to callback + OPC_CALLBACK mObjCallback; //!< Object callback + // Stats + udword mNbSphereBVTests; //!< Number of Sphere-BV tests + udword mNbSpherePrimTests; //!< Number of Sphere-Primitive tests + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + // Overlap tests + bool SphereAABBOverlap(const Point& center, const Point& extents); + bool SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2, float& sqrdist, float& u, float& v); + // Init methods + void InitQuery(const Sphere& sphere, const Matrix4x4& worlds, const Matrix4x4& worldm); + bool CheckTemporalCoherence(udword* faceid); + }; + +#endif // __OPC_SPHERECOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_SphereTriOverlap.h b/OpcodeDistrib/OPC_SphereTriOverlap.h new file mode 100644 index 0000000..3b26271 --- /dev/null +++ b/OpcodeDistrib/OPC_SphereTriOverlap.h @@ -0,0 +1,237 @@ + +// Original code by David Eberly in Magic. +bool AABBSphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2, float& sqrdist, float& u, float& v) +{ + // Stats + mNbSpherePrimTests++; + + Point TriOrigin = vert0; + Point TriEdge0 = vert1 - vert0; + Point TriEdge1 = vert2 - vert0; + + Point kDiff = TriOrigin - 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 fDet = fabsf(fA00*fA11 - fA01*fA01); + u = fA01*fB1-fA11*fB0; + v = fA01*fB0-fA00*fB1; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + 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; + } + 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; + } + 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; + } + else + { + u = -fB0/fA00; + sqrdist = fB0*u+fC; + } + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { + u = 0.0f; + v = 0.0f; + sqrdist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + u *= fInvDet; + v *= fInvDet; + sqrdist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(u < 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) + { + u = 1.0f; + v = 0.0f; + sqrdist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + sqrdist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + 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; + } + else + { + v = -fB1/fA11; + sqrdist = fB1*v+fC; + } + } + } + else if(v < 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) + { + v = 1.0f; + u = 0.0f; + sqrdist = fA11+2.0f*fB1+fC; + } + else + { + v = fNumer/fDenom; + u = 1.0f - v; + sqrdist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + 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; + } + else + { + u = -fB0/fA00; + sqrdist = fB0*u+fC; + } + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + u = 0.0f; + v = 1.0f; + sqrdist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + u = 1.0f; + v = 0.0f; + sqrdist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + sqrdist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + } + + sqrdist = fabsf(sqrdist); + + return sqrdist < mRadius2; +} diff --git a/OpcodeDistrib/OPC_TreeBuilders.h b/OpcodeDistrib/OPC_TreeBuilders.h index 48a0305..539c5e6 100644 --- a/OpcodeDistrib/OPC_TreeBuilders.h +++ b/OpcodeDistrib/OPC_TreeBuilders.h @@ -72,6 +72,7 @@ __forceinline void SetCount(udword nb) { mCount=nb; } __forceinline void IncreaseCount(udword nb) { mCount+=nb; } __forceinline udword GetCount() const { return mCount; } + private: udword mCount; //!< Stats: number of nodes created }; @@ -80,30 +81,30 @@ { public: //! Constructor - AABBTreeOfAABBsBuilder() : mAABBList(null) {} + AABBTreeOfAABBsBuilder() : mAABBList(null) {} //! Destructor - virtual ~AABBTreeOfAABBsBuilder() {} + virtual ~AABBTreeOfAABBsBuilder() {} - virtual bool ComputeGlobalBox(udword* primitives, udword nbprims, AABB& globalbox) const; - virtual float GetSplittingValue(udword index, udword axis) const; + override(AABBTreeBuilder) bool ComputeGlobalBox(udword* primitives, udword nbprims, AABB& globalbox) const; + override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const; - const AABB* mAABBList; //!< Shortcut to an app-controlled list of AABBs. + const AABB* mAABBList; //!< Shortcut to an app-controlled list of AABBs. }; class OPCODE_API AABBTreeOfTrianglesBuilder : public AABBTreeBuilder { public: //! Constructor - AABBTreeOfTrianglesBuilder() : mTriList(null), mNbTriangles(0) {} + AABBTreeOfTrianglesBuilder() : mTriList(null), mNbTriangles(0) {} //! Destructor - virtual ~AABBTreeOfTrianglesBuilder() {} + virtual ~AABBTreeOfTrianglesBuilder() {} - virtual bool ComputeGlobalBox(udword* primitives, udword nbprims, AABB& globalbox) const; - virtual float GetSplittingValue(udword index, udword axis) const; + override(AABBTreeBuilder) bool ComputeGlobalBox(udword* primitives, udword nbprims, AABB& globalbox) const; + override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const; - const Triangle* 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. + const Triangle* 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_TreeCollider.cpp b/OpcodeDistrib/OPC_TreeCollider.cpp index 9685ed3..8d69752 100644 --- a/OpcodeDistrib/OPC_TreeCollider.cpp +++ b/OpcodeDistrib/OPC_TreeCollider.cpp @@ -18,6 +18,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains an AABB tree collider. + * This class performs a collision test between two AABB trees. * * \class AABBTreeCollider * \author Pierre Terdiman @@ -40,614 +41,49 @@ __forceinline void TransformPoint(Point& dest, const Point* source, const Matrix dest.z = trans.z + source->x * rot.m[0][2] + source->y * rot.m[1][2] + source->z * rot.m[2][2]; } -//! Use CPU comparisons (comment that line to use standard FPU compares) -#define CPU_COMPARE +#include "OPC_BoxBoxOverlap.h" +#include "OPC_TriBoxOverlap.h" +#include "OPC_TriTriOverlap.h" /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * OBB-OBB overlap test using the separating axis theorem. - * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID) - * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion) - * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory) - * - Class III axes can be disabled... (SOLID & Intel fashion) - * - ...or enabled to perform some profiling - * - CPU comparisons used when appropriate - * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID) - * - * \param a [in] extent from box A - * \param Pa [in] center from box A - * \param b [in] extent from box B - * \param Pb [in] center from box B - * \return true if boxes overlap - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -__forceinline bool AABBTreeCollider::BoxBoxOverlap(const Point& a, const Point& Pa, const Point& b, const Point& Pb) -{ - // Stats - mNbBVBVTests++; - - float t,t2; - - // Class I : A's basis vectors -#ifdef CPU_COMPARE - float Tx = (mR1to0.m[0][0]*Pb.x + mR1to0.m[1][0]*Pb.y + mR1to0.m[2][0]*Pb.z) + mT1to0.x - Pa.x; - t = a.x + b.x*mAR.m[0][0] + b.y*mAR.m[1][0] + b.z*mAR.m[2][0]; - if(AIR(Tx) > IR(t)) return false; - - float Ty = (mR1to0.m[0][1]*Pb.x + mR1to0.m[1][1]*Pb.y + mR1to0.m[2][1]*Pb.z) + mT1to0.y - Pa.y; - t = a.y + b.x*mAR.m[0][1] + b.y*mAR.m[1][1] + b.z*mAR.m[2][1]; - if(AIR(Ty) > IR(t)) return false; - - float Tz = (mR1to0.m[0][2]*Pb.x + mR1to0.m[1][2]*Pb.y + mR1to0.m[2][2]*Pb.z) + mT1to0.z - Pa.z; - t = a.z + b.x*mAR.m[0][2] + b.y*mAR.m[1][2] + b.z*mAR.m[2][2]; - if(AIR(Tz) > IR(t)) return false; -#else - float Tx = (mR1to0.m[0][0]*Pb.x + mR1to0.m[1][0]*Pb.y + mR1to0.m[2][0]*Pb.z) + mT1to0.x - Pa.x; - t = a.x + b.x*mAR.m[0][0] + b.y*mAR.m[1][0] + b.z*mAR.m[2][0]; - if(fabsf(Tx) > t) return false; - - float Ty = (mR1to0.m[0][1]*Pb.x + mR1to0.m[1][1]*Pb.y + mR1to0.m[2][1]*Pb.z) + mT1to0.y - Pa.y; - t = a.y + b.x*mAR.m[0][1] + b.y*mAR.m[1][1] + b.z*mAR.m[2][1]; - if(fabsf(Ty) > t) return false; - - float Tz = (mR1to0.m[0][2]*Pb.x + mR1to0.m[1][2]*Pb.y + mR1to0.m[2][2]*Pb.z) + mT1to0.z - Pa.z; - t = a.z + b.x*mAR.m[0][2] + b.y*mAR.m[1][2] + b.z*mAR.m[2][2]; - if(fabsf(Tz) > t) return false; -#endif - - // Class II : B's basis vectors -#ifdef CPU_COMPARE - t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = a.x*mAR.m[0][0] + a.y*mAR.m[0][1] + a.z*mAR.m[0][2] + b.x; - if(AIR(t)>IR(t2)) return false; - - t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = a.x*mAR.m[1][0] + a.y*mAR.m[1][1] + a.z*mAR.m[1][2] + b.y; - if(AIR(t)>IR(t2)) return false; - - t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = a.x*mAR.m[2][0] + a.y*mAR.m[2][1] + a.z*mAR.m[2][2] + b.z; - if(AIR(t)>IR(t2)) return false; -#else - t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = a.x*mAR.m[0][0] + a.y*mAR.m[0][1] + a.z*mAR.m[0][2] + b.x; - if(fabsf(t) > t2) return false; - - t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = a.x*mAR.m[1][0] + a.y*mAR.m[1][1] + a.z*mAR.m[1][2] + b.y; - if(fabsf(t) > t2) return false; - - t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = a.x*mAR.m[2][0] + a.y*mAR.m[2][1] + a.z*mAR.m[2][2] + b.z; - if(fabsf(t) > t2) return false; -#endif - - // Class III : 9 cross products - // Cool trick: always perform the full test for first level, regardless of settings. - // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! - if(mFullBoxBoxTest || mNbBVBVTests==1) - { -#ifdef CPU_COMPARE - t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = a.y*mAR.m[0][2] + a.z*mAR.m[0][1] + b.y*mAR.m[2][0] + b.z*mAR.m[1][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B0 - t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = a.y*mAR.m[1][2] + a.z*mAR.m[1][1] + b.x*mAR.m[2][0] + b.z*mAR.m[0][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B1 - t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = a.y*mAR.m[2][2] + a.z*mAR.m[2][1] + b.x*mAR.m[1][0] + b.y*mAR.m[0][0]; if(AIR(t) > IR(t2)) return false; // L = A0 x B2 - t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = a.x*mAR.m[0][2] + a.z*mAR.m[0][0] + b.y*mAR.m[2][1] + b.z*mAR.m[1][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B0 - t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = a.x*mAR.m[1][2] + a.z*mAR.m[1][0] + b.x*mAR.m[2][1] + b.z*mAR.m[0][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B1 - t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = a.x*mAR.m[2][2] + a.z*mAR.m[2][0] + b.x*mAR.m[1][1] + b.y*mAR.m[0][1]; if(AIR(t) > IR(t2)) return false; // L = A1 x B2 - t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = a.x*mAR.m[0][1] + a.y*mAR.m[0][0] + b.y*mAR.m[2][2] + b.z*mAR.m[1][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B0 - t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = a.x*mAR.m[1][1] + a.y*mAR.m[1][0] + b.x*mAR.m[2][2] + b.z*mAR.m[0][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B1 - t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = a.x*mAR.m[2][1] + a.y*mAR.m[2][0] + b.x*mAR.m[1][2] + b.y*mAR.m[0][2]; if(AIR(t) > IR(t2)) return false; // L = A2 x B2 -#else - t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = a.y*mAR.m[0][2] + a.z*mAR.m[0][1] + b.y*mAR.m[2][0] + b.z*mAR.m[1][0]; if(fabsf(t) > t2) return false; - t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = a.y*mAR.m[1][2] + a.z*mAR.m[1][1] + b.x*mAR.m[2][0] + b.z*mAR.m[0][0]; if(fabsf(t) > t2) return false; - t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = a.y*mAR.m[2][2] + a.z*mAR.m[2][1] + b.x*mAR.m[1][0] + b.y*mAR.m[0][0]; if(fabsf(t) > t2) return false; - t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = a.x*mAR.m[0][2] + a.z*mAR.m[0][0] + b.y*mAR.m[2][1] + b.z*mAR.m[1][1]; if(fabsf(t) > t2) return false; - t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = a.x*mAR.m[1][2] + a.z*mAR.m[1][0] + b.x*mAR.m[2][1] + b.z*mAR.m[0][1]; if(fabsf(t) > t2) return false; - t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = a.x*mAR.m[2][2] + a.z*mAR.m[2][0] + b.x*mAR.m[1][1] + b.y*mAR.m[0][1]; if(fabsf(t) > t2) return false; - t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = a.x*mAR.m[0][1] + a.y*mAR.m[0][0] + b.y*mAR.m[2][2] + b.z*mAR.m[1][2]; if(fabsf(t) > t2) return false; - t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = a.x*mAR.m[1][1] + a.y*mAR.m[1][0] + b.x*mAR.m[2][2] + b.z*mAR.m[0][2]; if(fabsf(t) > t2) return false; - t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = a.x*mAR.m[2][1] + a.y*mAR.m[2][0] + b.x*mAR.m[1][2] + b.y*mAR.m[0][2]; if(fabsf(t) > t2) return false; -#endif - } - return true; -} - -//! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++) -#define USE_FCOMI - -//! This macro quickly finds the min & max values among 3 variables -#define FINDMINMAX(x0, x1, x2, min, max) \ - min = max = x0; \ - if(x1max) max=x1; \ - if(x2max) max=x2; - -//! TO BE DOCUMENTED -__forceinline bool planeBoxOverlap(const Point& normal, const float d, const Point& maxbox) -{ - Point vmin, vmax; - for(udword q=0;q<=2;q++) - { - if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; } - else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; } - } - if((normal|vmin)+d>0.0f) return false; - if((normal|vmax)+d>0.0f) return true; - - return false; -} - -//! TO BE DOCUMENTED -#define AXISTEST_X01(a, b, fa, fb) \ - min = a*v0.y - b*v0.z; \ - max = a*v2.y - b*v2.z; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.y + fb * extents.z; \ - if(min>rad || max<-rad) return false; - -//! TO BE DOCUMENTED -#define AXISTEST_X2(a, b, fa, fb) \ - min = a*v0.y - b*v0.z; \ - max = a*v1.y - b*v1.z; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.y + fb * extents.z; \ - if(min>rad || max<-rad) return false; - -//! TO BE DOCUMENTED -#define AXISTEST_Y02(a, b, fa, fb) \ - min = b*v0.z - a*v0.x; \ - max = b*v2.z - a*v2.x; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.x + fb * extents.z; \ - if(min>rad || max<-rad) return false; - -//! TO BE DOCUMENTED -#define AXISTEST_Y1(a, b, fa, fb) \ - min = b*v0.z - a*v0.x; \ - max = b*v1.z - a*v1.x; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.x + fb * extents.z; \ - if(min>rad || max<-rad) return false; - -//! TO BE DOCUMENTED -#define AXISTEST_Z12(a, b, fa, fb) \ - min = a*v1.x - b*v1.y; \ - max = a*v2.x - b*v2.y; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.x + fb * extents.y; \ - if(min>rad || max<-rad) return false; - -//! TO BE DOCUMENTED -#define AXISTEST_Z0(a, b, fa, fb) \ - min = a*v0.x - b*v0.y; \ - max = a*v1.x - b*v1.y; \ - if(min>max) {const float tmp=max; max=min; min=tmp; } \ - rad = fa * extents.x + fb * extents.y; \ - if(min>rad || max<-rad) return false; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Triangle-Box overlap test using the separating axis theorem. - * This is the code from Tomas Möller, a bit optimized: - * - with some more lazy evaluation (faster path on PC) - * - with a tiny bit of assembly - * - with "SAT-lite" applied if needed - * - and perhaps with some more minor modifs... - * - * \param center [in] box center - * \param extents [in] box extents - * \return true if triangle & box overlap - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -__forceinline bool AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents) -{ - // Stats - mNbBVPrimTests++; - - // use separating axis theorem to test overlap between triangle and box - // need to test for overlap in these directions: - // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle - // we do not even need to test these) - // 2) normal of the triangle - // 3) crossproduct(edge from tri, {x,y,z}-directin) - // this gives 3x3=9 more tests - - // move everything so that the boxcenter is in (0,0,0) - Point v0, v1, v2; - v0.x = mLeafVerts[0].x - center.x; - v1.x = mLeafVerts[1].x - center.x; - v2.x = mLeafVerts[2].x - center.x; - - // First, test overlap in the {x,y,z}-directions -#ifdef USE_FCOMI - // find min, max of the triangle in x-direction, and test for overlap in X - if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return false; - if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return false; - - // same for Y - v0.y = mLeafVerts[0].y - center.y; - v1.y = mLeafVerts[1].y - center.y; - v2.y = mLeafVerts[2].y - center.y; - - if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return false; - if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return false; - - // same for Z - v0.z = mLeafVerts[0].z - center.z; - v1.z = mLeafVerts[1].z - center.z; - v2.z = mLeafVerts[2].z - center.z; - - if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return false; - if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return false; -#else - float min,max; - // Find min, max of the triangle in x-direction, and test for overlap in X - FINDMINMAX(v0.x, v1.x, v2.x, min, max); - if(min>extents.x || max<-extents.x) return false; - - // Same for Y - v0.y = mLeafVerts[0].y - center.y; - v1.y = mLeafVerts[1].y - center.y; - v2.y = mLeafVerts[2].y - center.y; - - FINDMINMAX(v0.y, v1.y, v2.y, min, max); - if(min>extents.y || max<-extents.y) return false; - - // Same for Z - v0.z = mLeafVerts[0].z - center.z; - v1.z = mLeafVerts[1].z - center.z; - v2.z = mLeafVerts[2].z - center.z; - - FINDMINMAX(v0.z, v1.z, v2.z, min, max); - if(min>extents.z || max<-extents.z) return false; -#endif - // 2) Test if the box intersects the plane of the triangle - // compute plane equation of triangle: normal*x+d=0 - // ### could be precomputed since we use the same leaf triangle several times - const Point e0 = v1 - v0; - const Point e1 = v2 - v1; - const Point normal = e0 ^ e1; - const float d = -normal|v0; - if(!planeBoxOverlap(normal, d, extents)) return false; - - // 3) "Class III" tests - if(mFullPrimBoxTest) - { - float rad; - float min, max; - // compute triangle edges - // - edges lazy evaluated to take advantage of early exits - // - fabs precomputed (half less work, possible since extents are always >0) - // - customized macros to take advantage of the null component - // - axis vector discarded, possibly saves useless movs - - const float fey0 = fabsf(e0.y); - const float fez0 = fabsf(e0.z); - AXISTEST_X01(e0.z, e0.y, fez0, fey0); - const float fex0 = fabsf(e0.x); - AXISTEST_Y02(e0.z, e0.x, fez0, fex0); - AXISTEST_Z12(e0.y, e0.x, fey0, fex0); - - const float fey1 = fabsf(e1.y); - const float fez1 = fabsf(e1.z); - AXISTEST_X01(e1.z, e1.y, fez1, fey1); - const float fex1 = fabsf(e1.x); - AXISTEST_Y02(e1.z, e1.x, fez1, fex1); - AXISTEST_Z0(e1.y, e1.x, fey1, fex1); - - const Point e2 = mLeafVerts[0] - mLeafVerts[2]; - const float fey2 = fabsf(e2.y); - const float fez2 = fabsf(e2.z); - AXISTEST_X2(e2.z, e2.y, fez2, fey2); - const float fex2 = fabsf(e2.x); - AXISTEST_Y1(e2.z, e2.x, fez2, fex2); - AXISTEST_Z12(e2.y, e2.x, fey2, fex2); - } - return true; -} - -//! if USE_EPSILON_TEST is true then we do a check (if |dv|b) \ - { \ - const float c=a; \ - a=b; \ - b=c; \ - } - -//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202 -#define EDGE_EDGE_TEST(V0, U0, U1) \ - Bx = U0[i0] - U1[i0]; \ - By = U0[i1] - U1[i1]; \ - Cx = V0[i0] - U0[i0]; \ - Cy = V0[i1] - U0[i1]; \ - f = Ay*Bx - Ax*By; \ - d = By*Cx - Bx*Cy; \ - if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \ - { \ - const float e=Ax*Cy - Ay*Cx; \ - if(f>0.0f) \ - { \ - if(e>=0.0f && e<=f) return 1; \ - } \ - else \ - { \ - if(e<=0.0f && e>=f) return 1; \ - } \ - } - -//! TO BE DOCUMENTED -#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \ -{ \ - float Bx,By,Cx,Cy,d,f; \ - const float Ax = V1[i0] - V0[i0]; \ - const float Ay = V1[i1] - V0[i1]; \ - /* test edge U0,U1 against V0,V1 */ \ - EDGE_EDGE_TEST(V0, U0, U1); \ - /* test edge U1,U2 against V0,V1 */ \ - EDGE_EDGE_TEST(V0, U1, U2); \ - /* test edge U2,U1 against V0,V1 */ \ - EDGE_EDGE_TEST(V0, U2, U0); \ -} - -//! TO BE DOCUMENTED -#define POINT_IN_TRI(V0, U0, U1, U2) \ -{ \ - /* is T1 completly inside T2? */ \ - /* check if V0 is inside tri(U0,U1,U2) */ \ - float a = U1[i1] - U0[i1]; \ - float b = -(U1[i0] - U0[i0]); \ - float c = -a*U0[i0] - b*U0[i1]; \ - float d0 = a*V0[i0] + b*V0[i1] + c; \ - \ - a = U2[i1] - U1[i1]; \ - b = -(U2[i0] - U1[i0]); \ - c = -a*U1[i0] - b*U1[i1]; \ - const float d1 = a*V0[i0] + b*V0[i1] + c; \ - \ - a = U0[i1] - U2[i1]; \ - b = -(U0[i0] - U2[i0]); \ - c = -a*U2[i0] - b*U2[i1]; \ - const float d2 = a*V0[i0] + b*V0[i1] + c; \ - if(d0*d1>0.0f) \ - { \ - if(d0*d2>0.0f) return 1; \ - } \ -} - -//! TO BE DOCUMENTED -bool CoplanarTriTri(const Point& n, const Point& v0, const Point& v1, const Point& v2, const Point& u0, const Point& u1, const Point& u2) -{ - float A[3]; - short i0,i1; - /* first project onto an axis-aligned plane, that maximizes the area */ - /* of the triangles, compute indices: i0,i1. */ - A[0] = fabsf(n[0]); - A[1] = fabsf(n[1]); - A[2] = fabsf(n[2]); - if(A[0]>A[1]) - { - if(A[0]>A[2]) - { - i0=1; /* A[0] is greatest */ - i1=2; - } - else - { - i0=0; /* A[2] is greatest */ - i1=1; - } - } - else /* A[0]<=A[1] */ - { - if(A[2]>A[1]) - { - i0=0; /* A[2] is greatest */ - i1=1; - } - else - { - i0=0; /* A[1] is greatest */ - i1=2; - } - } - - /* test all edges of triangle 1 against the edges of triangle 2 */ - EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2); - EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2); - EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2); - - /* finally, test if tri1 is totally contained in tri2 or vice versa */ - POINT_IN_TRI(v0, u0, u1, u2); - POINT_IN_TRI(u0, v0, v1, v2); - - return 0; -} - -//! TO BE DOCUMENTED -#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \ -{ \ - if(D0D1>0.0f) \ - { \ - /* here we know that D0D2<=0.0 */ \ - /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ - A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ - } \ - else if(D0D2>0.0f) \ - { \ - /* here we know that d0d1<=0.0 */ \ - A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ - } \ - else if(D1*D2>0.0f || D0!=0.0f) \ - { \ - /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ - A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \ - } \ - else if(D1!=0.0f) \ - { \ - A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ - } \ - else if(D2!=0.0f) \ - { \ - A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ - } \ - else \ - { \ - /* triangles are coplanar */ \ - return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \ - } \ -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Triangle/triangle intersection test routine, - * by Tomas Moller, 1997. - * See article "A Fast Triangle-Triangle Intersection Test", - * Journal of Graphics Tools, 2(2), 1997 - * - * Updated June 1999: removed the divisions -- a little faster now! - * Updated October 1999: added {} to CROSS and SUB macros - * - * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3], - * float U0[3],float U1[3],float U2[3]) - * - * \param V0 [in] triangle 0, vertex 0 - * \param V1 [in] triangle 0, vertex 1 - * \param V2 [in] triangle 0, vertex 2 - * \param U0 [in] triangle 1, vertex 0 - * \param U1 [in] triangle 1, vertex 1 - * \param U2 [in] triangle 1, vertex 2 - * \return true if triangles overlap + * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -__forceinline bool AABBTreeCollider::TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2) +AABBTreeCollider::AABBTreeCollider() + : mNbBVBVTests (0), + mNbPrimPrimTests (0), + mNbBVPrimTests (0), + mFullBoxBoxTest (true), + mFullPrimBoxTest (true), + mUserData0 (0), + mUserData1 (0), + mObj0Callback (null), + mObj1Callback (null) { - // Stats - mNbPrimPrimTests++; - - // Compute plane equation of triangle(V0,V1,V2) - Point E1 = V1 - V0; - Point E2 = V2 - V0; - const Point N1 = E1 ^ E2; - const float d1 =-N1 | V0; - // Plane equation 1: N1.X+d1=0 - - // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane - float du0 = (N1|U0) + d1; - float du1 = (N1|U1) + d1; - float du2 = (N1|U2) + d1; - - // Coplanarity robustness check -#ifdef USE_EPSILON_TEST - if(fabsf(du0)0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? - return 0; // no intersection occurs - - // Compute plane of triangle (U0,U1,U2) - E1 = U1 - U0; - E2 = U2 - U0; - const Point N2 = E1 ^ E2; - const float d2=-N2 | U0; - // plane equation 2: N2.X+d2=0 - - // put V0,V1,V2 into plane equation 2 - float dv0 = (N2|V0) + d2; - float dv1 = (N2|V1) + d2; - float dv2 = (N2|V2) + d2; - -#ifdef USE_EPSILON_TEST - if(fabsf(dv0)0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? - return 0; // no intersection occurs - - // Compute direction of intersection line - const Point D = N1^N2; - - // Compute and index to the largest component of D - float max=fabsf(D[0]); - short index=0; - float bb=fabsf(D[1]); - float cc=fabsf(D[2]); - if(bb>max) max=bb,index=1; - if(cc>max) max=cc,index=2; - - // This is the simplified projection onto L - const float vp0 = V0[index]; - const float vp1 = V1[index]; - const float vp2 = V2[index]; - - const float up0 = U0[index]; - const float up1 = U1[index]; - const float up2 = U2[index]; - - // Compute interval for triangle 1 - float a,b,c,x0,x1; - NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1); - - // Compute interval for triangle 2 - float d,e,f,y0,y1; - NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1); - - const float xx=x0*x1; - const float yy=y0*y1; - const float xxyy=xx*yy; - - float isect1[2], isect2[2]; - - float tmp=a*xxyy; - isect1[0]=tmp+b*x1*yy; - isect1[1]=tmp+c*x0*yy; - - tmp=d*xxyy; - isect2[0]=tmp+e*xx*y1; - isect2[1]=tmp+f*xx*y0; - - SORT(isect1[0],isect1[1]); - SORT(isect2[0],isect2[1]); - - if(isect1[1]HasLeafNodes()!=cache.Model1->HasLeafNodes()) return false; if(cache.Model0->IsQuantized()!=cache.Model1->IsQuantized()) return false; + /* + + Rules: + - perform hull test + - when hulls collide, disable hull test + - if meshes overlap, reset countdown + - if countdown reaches 0, enable hull test + + */ + +#ifdef __MESHMERIZER_H__ + // Handle hulls + if(cache.HullTest) + { + if(cache.Model0->GetHull() && cache.Model1->GetHull()) + { + struct Local + { + static Point* SVCallback(const Point& sv, udword& previndex, udword userdata) + { + CollisionHull* Hull = (CollisionHull*)userdata; + previndex = Hull->ComputeSupportingVertex(sv, previndex); + return &Hull->GetVerts()[previndex]; + } + }; + + bool Collide; + + if(0) + { + static GJKEngine GJK; + static bool GJKInitDone=false; + if(!GJKInitDone) + { + GJK.Enable(GJK_BACKUP_PROCEDURE); + GJK.Enable(GJK_DEGENERATE); + GJK.Enable(GJK_HILLCLIMBING); + GJKInitDone = true; + } + GJK.SetCallbackObj0(Local::SVCallback); + GJK.SetCallbackObj1(Local::SVCallback); + GJK.SetUserData0(udword(cache.Model0->GetHull())); + GJK.SetUserData1(udword(cache.Model1->GetHull())); + Collide = GJK.Collide(world0, world1, &cache.SepVector); + } + else + { + static SVEngine SVE; + SVE.SetCallbackObj0(Local::SVCallback); + SVE.SetCallbackObj1(Local::SVCallback); + SVE.SetUserData0(udword(cache.Model0->GetHull())); + SVE.SetUserData1(udword(cache.Model1->GetHull())); + Collide = SVE.Collide(world0, world1, &cache.SepVector); + } + + if(!Collide) + { + // Reset stats & contact status + mContact = false; + mNbBVBVTests = 0; + mNbPrimPrimTests = 0; + mNbBVPrimTests = 0; + mPairs.Reset(); + return true; + } + } + } + + // Here, hulls collide + cache.HullTest = false; +#endif // __MESHMERIZER_H__ + // Simple double-dispatch + bool Status; if(!cache.Model0->HasLeafNodes()) { if(cache.Model0->IsQuantized()) { const AABBQuantizedNoLeafTree* T0 = (const AABBQuantizedNoLeafTree*)cache.Model0->GetTree(); const AABBQuantizedNoLeafTree* T1 = (const AABBQuantizedNoLeafTree*)cache.Model1->GetTree(); - return Collide(T0, T1, world0, world1, &cache); + Status = Collide(T0, T1, world0, world1, &cache); } else { const AABBNoLeafTree* T0 = (const AABBNoLeafTree*)cache.Model0->GetTree(); const AABBNoLeafTree* T1 = (const AABBNoLeafTree*)cache.Model1->GetTree(); - return Collide(T0, T1, world0, world1, &cache); + Status = Collide(T0, T1, world0, world1, &cache); } } else @@ -693,15 +202,32 @@ bool AABBTreeCollider::Collide(BVTCache& cache, const Matrix4x4& world0, const M { const AABBQuantizedTree* T0 = (const AABBQuantizedTree*)cache.Model0->GetTree(); const AABBQuantizedTree* T1 = (const AABBQuantizedTree*)cache.Model1->GetTree(); - return Collide(T0, T1, world0, world1, &cache); + Status = Collide(T0, T1, world0, world1, &cache); } else { const AABBCollisionTree* T0 = (const AABBCollisionTree*)cache.Model0->GetTree(); const AABBCollisionTree* T1 = (const AABBCollisionTree*)cache.Model1->GetTree(); - return Collide(T0, T1, world0, world1, &cache); + Status = Collide(T0, T1, world0, world1, &cache); + } + } + +#ifdef __MESHMERIZER_H__ + if(Status) + { + // Reset counter as long as overlap occurs + if(GetContactStatus()) cache.ResetCountDown(); + + // Enable hull test again when counter reaches zero + cache.CountDown--; + if(!cache.CountDown) + { + cache.ResetCountDown(); + cache.HullTest = true; } } +#endif + return Status; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -961,6 +487,7 @@ void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollision } #endif + #ifdef ALTERNATIVE_CODE /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -969,6 +496,7 @@ 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 @@ -1359,7 +887,7 @@ void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuan { FETCH_LEAF(a->GetPrimitive(), mObj0Callback, mUserData0, mR0to1, mT0to1) - if(BHasLeaf) PrimTestTriIndex( b->GetPrimitive()); + if(BHasLeaf) PrimTestTriIndex(b->GetPrimitive()); else _CollideTriBox(b->GetPos()); if(mFirstContact && mContact) return; diff --git a/OpcodeDistrib/OPC_TreeCollider.h b/OpcodeDistrib/OPC_TreeCollider.h index adc6bc1..4abedbd 100644 --- a/OpcodeDistrib/OPC_TreeCollider.h +++ b/OpcodeDistrib/OPC_TreeCollider.h @@ -20,38 +20,12 @@ #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 - __forceinline BVTCache() - { - ResetCache(); - } - - void ResetCache() - { - Model0 = null; - Model1 = null; - id0 = 0; - id1 = 1; - } - - OPCODE_Model* Model0; //!< Model for first object - OPCODE_Model* Model1; //!< Model for second object - }; - - class OPCODE_API AABBTreeCollider + class OPCODE_API AABBTreeCollider : public Collider { public: // Constructor / Destructor - AABBTreeCollider(); - ~AABBTreeCollider(); + AABBTreeCollider(); + virtual ~AABBTreeCollider(); // Generic collision query /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -68,13 +42,14 @@ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool Collide(BVTCache& cache, const Matrix4x4& world0, const Matrix4x4& world1); + bool Collide(BVTCache& cache, const Matrix4x4& world0, const Matrix4x4& world1); // Collision queries - bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); - bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); - bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); - bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); + bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); + bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); + bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); + bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4& world0, const Matrix4x4& world1, Pair* cache=null); + // Settings /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -82,45 +57,18 @@ * Settings: select between full box-box tests or "SAT-lite" tests (where Class III axes are discarded) * \param flag [in] true for full tests, false for coarse tests * \see SetFullPrimBoxTest(bool flag) - * \see SetFirstContact(bool flag) - * \see SetTemporalCoherence(bool flag) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } + __forceinline void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Settings: select between full triangle-box tests or "SAT-lite" tests (where Class III axes are discarded) * \param flag [in] true for full tests, false for coarse tests * \see SetFullBoxBoxTest(bool flag) - * \see SetFirstContact(bool flag) - * \see SetTemporalCoherence(bool flag) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Settings: reports all contacts (false) or first contact only (true) - * \param flag [in] true for first contact, false for all contacts - * \see SetFullBoxBoxTest(bool flag) - * \see SetFullPrimBoxTest(bool flag) - * \see SetTemporalCoherence(bool flag) - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline void SetFirstContact(bool flag) { mFirstContact = flag; } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Settings: test pairs of colliding triangles from previous frame before anything else - * \param flag [in] true to enable temporal coherence, false to discard it - * \warning Only works in "First contact" mode, and currently wouldn't work in an N-body system (last pair is cached in AABBTreeCollider) - * \see SetFullBoxBoxTest(bool flag) - * \see SetFullPrimBoxTest(bool flag) - * \see SetFirstContact(bool flag) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline void SetTemporalCoherence(bool flag) { mTemporalCoherence = flag; } + __forceinline void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; } // Stats @@ -132,7 +80,7 @@ * \return the number of BV-BV tests performed during last query */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline udword GetNbBVBVTests() const { return mNbBVBVTests; } + __forceinline udword GetNbBVBVTests() const { return mNbBVBVTests; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -142,7 +90,7 @@ * \return the number of Triangle-Triangle tests performed during last query */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; } + __forceinline udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -152,7 +100,7 @@ * \return the number of BV-Triangle tests performed during last query */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline udword GetNbBVPrimTests() const { return mNbBVPrimTests; } + __forceinline udword GetNbBVPrimTests() const { return mNbBVPrimTests; } // Data access @@ -164,7 +112,7 @@ * \return the number of contacts / colliding pairs. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; } + __forceinline udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -174,17 +122,7 @@ * \return the list of colliding pairs (triangle indices) */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline Pair* GetPairs() const { return (Pair*)mPairs.GetEntries(); } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** - * A method to get the last collision status after a collision query. - * \see GetPairs() - * \see GetNbPairs() - * \return true if the objects overlap - */ - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline bool GetContactStatus() const { return mContact; } + __forceinline Pair* GetPairs() const { return (Pair*)mPairs.GetEntries(); } // Callback control @@ -196,7 +134,7 @@ * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline AABBTreeCollider& SetUserData0(udword data) { mUserData0 = data; return *this; } + __forceinline AABBTreeCollider& SetUserData0(udword data) { mUserData0 = data; return *this; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -206,7 +144,7 @@ * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline AABBTreeCollider& SetUserData1(udword data) { mUserData1 = data; return *this; } + __forceinline AABBTreeCollider& SetUserData1(udword data) { mUserData1 = data; return *this; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -216,7 +154,7 @@ * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline AABBTreeCollider& SetCallbackObj0(OPC_CALLBACK callback) { mObj0Callback = callback; return *this; } + __forceinline AABBTreeCollider& SetCallbackObj0(OPC_CALLBACK callback) { mObj0Callback = callback; return *this; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -226,66 +164,70 @@ * \return Self-Reference */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - __forceinline AABBTreeCollider& SetCallbackObj1(OPC_CALLBACK callback) { mObj1Callback = callback; return *this; } + __forceinline AABBTreeCollider& SetCallbackObj1(OPC_CALLBACK callback) { mObj1Callback = callback; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * 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(); private: // Colliding pairs - Container mPairs; //!< Pairs of colliding primitives + Container mPairs; //!< Pairs of colliding primitives // User callback - udword mUserData0; //!< User-defined data sent to callbacks - udword mUserData1; //!< User-defined data sent to callbacks - OPC_CALLBACK mObj0Callback; //!< Callback for object 0 - OPC_CALLBACK mObj1Callback; //!< Callback for object 1 + udword mUserData0; //!< User-defined data sent to callbacks + udword mUserData1; //!< User-defined data sent to callbacks + OPC_CALLBACK mObj0Callback; //!< Callback for object 0 + OPC_CALLBACK mObj1Callback; //!< Callback for object 1 // Stats - udword mNbBVBVTests; //!< Number of BV-BV tests - udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests - udword mNbBVPrimTests; //!< Number of BV-Primitive tests + udword mNbBVBVTests; //!< Number of BV-BV tests + udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests + udword mNbBVPrimTests; //!< Number of BV-Primitive tests // Precomputed data - Matrix3x3 mAR; //!< Absolute rotation matrix - Matrix3x3 mR0to1; //!< Rotation from object0 to object1 - Matrix3x3 mR1to0; //!< Rotation from object1 to object0 - Point mT0to1; //!< Translation from object0 to object1 - Point mT1to0; //!< Translation from object1 to object0 + Matrix3x3 mAR; //!< Absolute rotation matrix + Matrix3x3 mR0to1; //!< Rotation from object0 to object1 + Matrix3x3 mR1to0; //!< Rotation from object1 to object0 + Point mT0to1; //!< Translation from object0 to object1 + Point mT1to0; //!< Translation from object1 to object0 // Dequantization coeffs - Point mCenterCoeff0; - Point mExtentsCoeff0; - Point mCenterCoeff1; - Point mExtentsCoeff1; + Point mCenterCoeff0; + Point mExtentsCoeff0; + Point mCenterCoeff1; + Point mExtentsCoeff1; // Leaf description - Point mLeafVerts[3]; //!< Triangle vertices - udword mLeafIndex; //!< Triangle index + Point mLeafVerts[3]; //!< Triangle vertices + udword mLeafIndex; //!< Triangle index // Settings - bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) - bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false) - bool mFirstContact; //!< Report all contacts (false) or only first one (true) - bool mTemporalCoherence; //!< Use temporal coherence or not - // Collision result - bool mContact; //!< Final contact status after a collision query + bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) + bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false) // Internal methods // Standard AABB trees - void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1); + void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1); // Quantized AABB trees - void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb); + void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb); // No-leaf AABB trees - void _CollideTriBox(const AABBNoLeafNode* b); - void _CollideBoxTri(const AABBNoLeafNode* b); - void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b); + void _CollideTriBox(const AABBNoLeafNode* b); + void _CollideBoxTri(const AABBNoLeafNode* b); + void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b); // Quantized no-leaf AABB trees - void _CollideTriBox(const AABBQuantizedNoLeafNode* b); - void _CollideBoxTri(const AABBQuantizedNoLeafNode* b); - void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b); + void _CollideTriBox(const AABBQuantizedNoLeafNode* b); + void _CollideBoxTri(const AABBQuantizedNoLeafNode* b); + void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b); // Overlap tests - void PrimTest(udword id0, udword id1); - void PrimTestTriIndex(udword id1); - void PrimTestIndexTri(udword id0); + void PrimTest(udword id0, udword id1); + void PrimTestTriIndex(udword id1); + void PrimTestIndexTri(udword id0); - bool BoxBoxOverlap(const Point& a, const Point& Pa, const Point& b, const Point& Pb); - bool TriBoxOverlap(const Point& center, const Point& extents); - bool TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2); + bool BoxBoxOverlap(const Point& a, const Point& Pa, const Point& b, const Point& Pb); + bool TriBoxOverlap(const Point& center, const Point& extents); + bool TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2); // Init methods - void InitQuery(const Matrix4x4& world0, const Matrix4x4& world1); - bool CheckTemporalCoherence(Pair* cache); + void InitQuery(const Matrix4x4& world0, const Matrix4x4& world1); + bool CheckTemporalCoherence(Pair* cache); }; #endif // __OPC_TREECOLLIDER_H__ diff --git a/OpcodeDistrib/OPC_TriBoxOverlap.h b/OpcodeDistrib/OPC_TriBoxOverlap.h new file mode 100644 index 0000000..b42b310 --- /dev/null +++ b/OpcodeDistrib/OPC_TriBoxOverlap.h @@ -0,0 +1,192 @@ + +//! This macro quickly finds the min & max values among 3 variables +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if(x1max) max=x1; \ + if(x2max) max=x2; + +//! TO BE DOCUMENTED +__forceinline bool planeBoxOverlap(const Point& normal, const float d, const Point& maxbox) +{ + Point vmin, vmax; + for(udword q=0;q<=2;q++) + { + if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; } + else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; } + } + if((normal|vmin)+d>0.0f) return false; + if((normal|vmax)+d>=0.0f) return true; + + return false; +} + +//! TO BE DOCUMENTED +#define AXISTEST_X01(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v2.y - b*v2.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return false; + +//! TO BE DOCUMENTED +#define AXISTEST_X2(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v1.y - b*v1.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return false; + +//! TO BE DOCUMENTED +#define AXISTEST_Y02(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v2.z - a*v2.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return false; + +//! TO BE DOCUMENTED +#define AXISTEST_Y1(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v1.z - a*v1.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return false; + +//! TO BE DOCUMENTED +#define AXISTEST_Z12(a, b, fa, fb) \ + min = a*v1.x - b*v1.y; \ + max = a*v2.x - b*v2.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return false; + +//! TO BE DOCUMENTED +#define AXISTEST_Z0(a, b, fa, fb) \ + min = a*v0.x - b*v0.y; \ + max = a*v1.x - b*v1.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return false; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle-Box overlap test using the separating axis theorem. + * This is the code from Tomas Möller, a bit optimized: + * - with some more lazy evaluation (faster path on PC) + * - with a tiny bit of assembly + * - with "SAT-lite" applied if needed + * - and perhaps with some more minor modifs... + * + * \param center [in] box center + * \param extents [in] box extents + * \return true if triangle & box overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +__forceinline bool AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbBVPrimTests++; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // move everything so that the boxcenter is in (0,0,0) + Point v0, v1, v2; + v0.x = mLeafVerts[0].x - center.x; + v1.x = mLeafVerts[1].x - center.x; + v2.x = mLeafVerts[2].x - center.x; + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return false; + if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return false; + + // same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return false; + if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return false; + + // same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return false; + if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return false; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>extents.x || max<-extents.x) return false; + + // Same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>extents.y || max<-extents.y) return false; + + // Same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>extents.z || max<-extents.z) return false; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, extents)) return false; + + // 3) "Class III" tests + if(mFullPrimBoxTest) + { + float rad; + float min, max; + // compute triangle edges + // - edges lazy evaluated to take advantage of early exits + // - fabs precomputed (half less work, possible since extents are always >0) + // - customized macros to take advantage of the null component + // - axis vector discarded, possibly saves useless movs + + const float fey0 = fabsf(e0.y); + const float fez0 = fabsf(e0.z); + AXISTEST_X01(e0.z, e0.y, fez0, fey0); + const float fex0 = fabsf(e0.x); + AXISTEST_Y02(e0.z, e0.x, fez0, fex0); + AXISTEST_Z12(e0.y, e0.x, fey0, fex0); + + const float fey1 = fabsf(e1.y); + const float fez1 = fabsf(e1.z); + AXISTEST_X01(e1.z, e1.y, fez1, fey1); + const float fex1 = fabsf(e1.x); + AXISTEST_Y02(e1.z, e1.x, fez1, fex1); + AXISTEST_Z0(e1.y, e1.x, fey1, fex1); + + const Point e2 = mLeafVerts[0] - mLeafVerts[2]; + const float fey2 = fabsf(e2.y); + const float fez2 = fabsf(e2.z); + AXISTEST_X2(e2.z, e2.y, fez2, fey2); + const float fex2 = fabsf(e2.x); + AXISTEST_Y1(e2.z, e2.x, fez2, fex2); + AXISTEST_Z12(e2.y, e2.x, fey2, fex2); + } + return true; +} diff --git a/OpcodeDistrib/OPC_TriTriOverlap.h b/OpcodeDistrib/OPC_TriTriOverlap.h new file mode 100644 index 0000000..3494dac --- /dev/null +++ b/OpcodeDistrib/OPC_TriTriOverlap.h @@ -0,0 +1,279 @@ + +//! if OPC_TRITRI_EPSILON_TEST is true then we do a check (if |dv|b) \ + { \ + const float c=a; \ + a=b; \ + b=c; \ + } + +//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202 +#define EDGE_EDGE_TEST(V0, U0, U1) \ + Bx = U0[i0] - U1[i0]; \ + By = U0[i1] - U1[i1]; \ + Cx = V0[i0] - U0[i0]; \ + Cy = V0[i1] - U0[i1]; \ + f = Ay*Bx - Ax*By; \ + d = By*Cx - Bx*Cy; \ + if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \ + { \ + const float e=Ax*Cy - Ay*Cx; \ + if(f>0.0f) \ + { \ + if(e>=0.0f && e<=f) return 1; \ + } \ + else \ + { \ + if(e<=0.0f && e>=f) return 1; \ + } \ + } + +//! TO BE DOCUMENTED +#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \ +{ \ + float Bx,By,Cx,Cy,d,f; \ + const float Ax = V1[i0] - V0[i0]; \ + const float Ay = V1[i1] - V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U0, U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U1, U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U2, U0); \ +} + +//! TO BE DOCUMENTED +#define POINT_IN_TRI(V0, U0, U1, U2) \ +{ \ + /* is T1 completly inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + float a = U1[i1] - U0[i1]; \ + float b = -(U1[i0] - U0[i0]); \ + float c = -a*U0[i0] - b*U0[i1]; \ + float d0 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U2[i1] - U1[i1]; \ + b = -(U2[i0] - U1[i0]); \ + c = -a*U1[i0] - b*U1[i1]; \ + const float d1 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U0[i1] - U2[i1]; \ + b = -(U0[i0] - U2[i0]); \ + c = -a*U2[i0] - b*U2[i1]; \ + const float d2 = a*V0[i0] + b*V0[i1] + c; \ + if(d0*d1>0.0f) \ + { \ + if(d0*d2>0.0f) return 1; \ + } \ +} + +//! TO BE DOCUMENTED +bool CoplanarTriTri(const Point& n, const Point& v0, const Point& v1, const Point& v2, const Point& u0, const Point& u1, const Point& u2) +{ + float A[3]; + short i0,i1; + /* first project onto an axis-aligned plane, that maximizes the area */ + /* of the triangles, compute indices: i0,i1. */ + A[0] = fabsf(n[0]); + A[1] = fabsf(n[1]); + A[2] = fabsf(n[2]); + if(A[0]>A[1]) + { + if(A[0]>A[2]) + { + i0=1; /* A[0] is greatest */ + i1=2; + } + else + { + i0=0; /* A[2] is greatest */ + i1=1; + } + } + else /* A[0]<=A[1] */ + { + if(A[2]>A[1]) + { + i0=0; /* A[2] is greatest */ + i1=1; + } + else + { + i0=0; /* A[1] is greatest */ + i1=2; + } + } + + /* test all edges of triangle 1 against the edges of triangle 2 */ + EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2); + + /* finally, test if tri1 is totally contained in tri2 or vice versa */ + POINT_IN_TRI(v0, u0, u1, u2); + POINT_IN_TRI(u0, v0, v1, v2); + + return 0; +} + +//! TO BE DOCUMENTED +#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \ +{ \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else if(D0D2>0.0f) \ + { \ + /* here we know that d0d1<=0.0 */ \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \ + } \ + else if(D1!=0.0f) \ + { \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D2!=0.0f) \ + { \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \ + } \ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle/triangle intersection test routine, + * by Tomas Moller, 1997. + * See article "A Fast Triangle-Triangle Intersection Test", + * Journal of Graphics Tools, 2(2), 1997 + * + * Updated June 1999: removed the divisions -- a little faster now! + * Updated October 1999: added {} to CROSS and SUB macros + * + * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3], + * float U0[3],float U1[3],float U2[3]) + * + * \param V0 [in] triangle 0, vertex 0 + * \param V1 [in] triangle 0, vertex 1 + * \param V2 [in] triangle 0, vertex 2 + * \param U0 [in] triangle 1, vertex 0 + * \param U1 [in] triangle 1, vertex 1 + * \param U2 [in] triangle 1, vertex 2 + * \return true if triangles overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +__forceinline bool AABBTreeCollider::TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2) +{ + // Stats + mNbPrimPrimTests++; + + // Compute plane equation of triangle(V0,V1,V2) + Point E1 = V1 - V0; + Point E2 = V2 - V0; + const Point N1 = E1 ^ E2; + const float d1 =-N1 | V0; + // Plane equation 1: N1.X+d1=0 + + // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane + float du0 = (N1|U0) + d1; + float du1 = (N1|U1) + d1; + float du2 = (N1|U2) + d1; + + // Coplanarity robustness check +#ifdef OPC_TRITRI_EPSILON_TEST + if(fabsf(du0)0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? + return 0; // no intersection occurs + + // Compute plane of triangle (U0,U1,U2) + E1 = U1 - U0; + E2 = U2 - U0; + const Point N2 = E1 ^ E2; + const float d2=-N2 | U0; + // plane equation 2: N2.X+d2=0 + + // put V0,V1,V2 into plane equation 2 + float dv0 = (N2|V0) + d2; + float dv1 = (N2|V1) + d2; + float dv2 = (N2|V2) + d2; + +#ifdef OPC_TRITRI_EPSILON_TEST + if(fabsf(dv0)0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? + return 0; // no intersection occurs + + // Compute direction of intersection line + const Point D = N1^N2; + + // Compute and index to the largest component of D + float max=fabsf(D[0]); + short index=0; + float bb=fabsf(D[1]); + float cc=fabsf(D[2]); + if(bb>max) max=bb,index=1; + if(cc>max) max=cc,index=2; + + // This is the simplified projection onto L + const float vp0 = V0[index]; + const float vp1 = V1[index]; + const float vp2 = V2[index]; + + const float up0 = U0[index]; + const float up1 = U1[index]; + const float up2 = U2[index]; + + // Compute interval for triangle 1 + float a,b,c,x0,x1; + NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1); + + // Compute interval for triangle 2 + float d,e,f,y0,y1; + NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1); + + const float xx=x0*x1; + const float yy=y0*y1; + const float xxyy=xx*yy; + + float isect1[2], isect2[2]; + + float tmp=a*xxyy; + isect1[0]=tmp+b*x1*yy; + isect1[1]=tmp+c*x0*yy; + + tmp=d*xxyy; + isect2[0]=tmp+e*xx*y1; + isect2[1]=tmp+f*xx*y0; + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + if(isect1[1] -#endif - namespace Opcode - { -#ifndef __ICECORE_H__ - #include "OPC_Container.h" -#endif + #define ICEMATHS_API OPCODE_API -#ifndef __ICEMATHS_H__ - #include "OPC_Point.h" - #include "OPC_Matrix3x3.h" - #include "OPC_Matrix4x4.h" + class HPoint; + class Matrix3x3; + class Matrix4x4; + class Quat; + class PRS; + class PR; + + // IceMaths includes + #include "IcePoint.h" + #include "IceHPoint.h" + #include "IceMatrix3x3.h" + #include "IceMatrix4x4.h" + #include "IceRay.h" #endif + #ifndef __MESHMERIZER_H__ - #include "OPC_Triangle.h" - #include "OPC_AABB.h" + + #define MESHMERIZER_API OPCODE_API + + enum CubeIndex; + class Plane; + class ProgressiveEigen; + + // Meshmerizer includes + #include "IceTriangle.h" + #include "IceAABB.h" + #include "IceBoundingSphere.h" #endif + namespace Opcode + { // Bulk-of-the-work + #include "OPC_Settings.h" #include "OPC_Common.h" #include "OPC_TreeBuilders.h" #include "OPC_AABBTree.h" #include "OPC_OptimizedTree.h" #include "OPC_Model.h" + #include "OPC_BVTCache.h" + #include "OPC_Collider.h" #include "OPC_TreeCollider.h" + #include "OPC_RayCollider.h" + #include "OPC_SphereCollider.h" } #endif // __OPCODE_H__ diff --git a/OpcodeDistrib/OpcodeDistrib.dsp b/OpcodeDistrib/OpcodeDistrib.dsp index 2fca210..d6673f7 100644 --- a/OpcodeDistrib/OpcodeDistrib.dsp +++ b/OpcodeDistrib/OpcodeDistrib.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 "OPCODEDISTRIB_EXPORTS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /G6 /MD /W3 /GX- /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODEDISTRIB_EXPORTS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /G6 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODEDISTRIB_EXPORTS" /Yu"stdafx.h" /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "NDEBUG" @@ -72,8 +72,8 @@ PostBuild_Cmds=xcopy release\Opcode.lib y:\lib /Q /Y xcopy *.h Y:\inc /Q /Y del # PROP Intermediate_Dir "Debug" # 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 "OPCODEDISTRIB_EXPORTS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /G6 /MDd /W3 /Gm /GX- /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODEDISTRIB_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODEDISTRIB_EXPORTS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /G6 /MDd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODEDISTRIB_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "_DEBUG" @@ -108,6 +108,10 @@ SOURCE=.\OPC_Model.h # End Source File # Begin Source File +SOURCE=.\OPC_Settings.h +# End Source File +# Begin Source File + SOURCE=.\Opcode.cpp # End Source File # Begin Source File @@ -127,6 +131,42 @@ SOURCE=.\StdAfx.h # Begin Group "Trees" # PROP Default_Filter "" +# Begin Group "Queries" + +# 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_RayCollider.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_RayCollider.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_SphereCollider.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_SphereCollider.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_TreeCollider.cpp +# End Source File +# Begin Source File + +SOURCE=.\OPC_TreeCollider.h +# End Source File +# End Group # Begin Source File SOURCE=.\OPC_AABBTree.cpp @@ -137,6 +177,10 @@ SOURCE=.\OPC_AABBTree.h # End Source File # Begin Source File +SOURCE=.\OPC_BVTCache.h +# End Source File +# Begin Source File + SOURCE=.\OPC_Common.cpp # End Source File # Begin Source File @@ -159,77 +203,121 @@ SOURCE=.\OPC_TreeBuilders.cpp SOURCE=.\OPC_TreeBuilders.h # End Source File +# End Group +# Begin Group "Overlap tests" + +# PROP Default_Filter "" # Begin Source File -SOURCE=.\OPC_TreeCollider.cpp +SOURCE=.\OPC_BoxBoxOverlap.h # End Source File # Begin Source File -SOURCE=.\OPC_TreeCollider.h +SOURCE=.\OPC_RayAABBOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_RayTriOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_SphereAABBOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_SphereTriOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_TriBoxOverlap.h +# End Source File +# Begin Source File + +SOURCE=.\OPC_TriTriOverlap.h # End Source File # End Group -# Begin Group "ICE utils" +# Begin Group "ICE code" # PROP Default_Filter "" # Begin Source File -SOURCE=.\OPC_AABB.cpp +SOURCE=.\IceAABB.cpp +# End Source File +# Begin Source File + +SOURCE=.\IceAABB.h +# End Source File +# Begin Source File + +SOURCE=.\IceBoundingSphere.h +# End Source File +# Begin Source File + +SOURCE=.\IceContainer.cpp +# End Source File +# Begin Source File + +SOURCE=.\IceContainer.h +# End Source File +# Begin Source File + +SOURCE=.\IceFPU.h # End Source File # Begin Source File -SOURCE=.\OPC_AABB.h +SOURCE=.\IceHPoint.h # End Source File # Begin Source File -SOURCE=.\OPC_Container.cpp +SOURCE=.\IceMatrix3x3.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Container.h +SOURCE=.\IceMatrix3x3.h # End Source File # Begin Source File -SOURCE=.\OPC_FPU.h +SOURCE=.\IceMatrix4x4.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Matrix3x3.cpp +SOURCE=.\IceMatrix4x4.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix3x3.h +SOURCE=.\IceMemoryMacros.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix4x4.cpp +SOURCE=.\IcePlane.h # End Source File # Begin Source File -SOURCE=.\OPC_Matrix4x4.h +SOURCE=.\IcePoint.cpp # End Source File # Begin Source File -SOURCE=.\OPC_MemoryMacros.h +SOURCE=.\IcePoint.h # End Source File # Begin Source File -SOURCE=.\OPC_Point.cpp +SOURCE=.\IceRandom.h # End Source File # Begin Source File -SOURCE=.\OPC_Point.h +SOURCE=.\IceRay.h # End Source File # Begin Source File -SOURCE=.\OPC_Triangle.cpp +SOURCE=.\IceTriangle.cpp # End Source File # Begin Source File -SOURCE=.\OPC_Triangle.h +SOURCE=.\IceTriangle.h # End Source File # Begin Source File -SOURCE=.\OPC_Types.h +SOURCE=.\IceTypes.h # End Source File # End Group # Begin Source File diff --git a/OpcodeDistrib/ReadMe.txt b/OpcodeDistrib/ReadMe.txt index 2173b7b..002a8e5 100644 --- a/OpcodeDistrib/ReadMe.txt +++ b/OpcodeDistrib/ReadMe.txt @@ -1,7 +1,33 @@ - OPCODE distribution 1.0 + OPCODE distribution 1.1 ----------------------- + 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. + + =============================================================================== + WHAT ? OPCODE means OPtimized COllision DEtection. @@ -26,6 +52,7 @@ - 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. WHY ? @@ -44,7 +71,7 @@ WHO ? Pierre Terdiman - May 03, 2001 + October 9, 2001 p.terdiman@wanadoo.fr p.terdiman@codercorner.com diff --git a/OpcodeDistrib/StdAfx.h b/OpcodeDistrib/StdAfx.h index 762e438..9988c25 100644 --- a/OpcodeDistrib/StdAfx.h +++ b/OpcodeDistrib/StdAfx.h @@ -1,10 +1,13 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#if !defined(AFX_STDAFX_H__F5D791A3_3FDF_11D5_8B0F_0050BAC83302__INCLUDED_) -#define AFX_STDAFX_H__F5D791A3_3FDF_11D5_8B0F_0050BAC83302__INCLUDED_ +#if !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_) +#define AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_ #if _MSC_VER > 1000 #pragma once @@ -13,29 +16,9 @@ // 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 "IceCore.h" - using namespace IceCore; - - #include "IceMaths.h" - using namespace IceMaths; - - #include "Meshmerizer.h" - using namespace Meshmerizer; - - #include "ZCollide.h" - using namespace ZCollide; - -#endif // OPCODE_USING_ICE - #include "Opcode.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__F5D791A3_3FDF_11D5_8B0F_0050BAC83302__INCLUDED_) +#endif // !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_) diff --git a/ReadMe.txt b/ReadMe.txt index 2173b7b..002a8e5 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -1,7 +1,33 @@ - OPCODE distribution 1.0 + OPCODE distribution 1.1 ----------------------- + 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. + + =============================================================================== + WHAT ? OPCODE means OPtimized COllision DEtection. @@ -26,6 +52,7 @@ - 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. WHY ? @@ -44,7 +71,7 @@ WHO ? Pierre Terdiman - May 03, 2001 + October 9, 2001 p.terdiman@wanadoo.fr p.terdiman@codercorner.com