Kaynağa Gözat

Bugfix: Sphere and AABox transforms now properly account for non-uniform scaling in the transform matrix

BearishSun 8 yıl önce
ebeveyn
işleme
8af92d17ba

+ 22 - 8
Source/BansheeUtility/Math/BsAABox.cpp

@@ -173,16 +173,30 @@ namespace bs
 	{
 		BS_ASSERT(m.isAffine());
 
-		Vector3 centre = getCenter();
-		Vector3 halfSize = getHalfSize();
+		Vector3 min = m.getTranslation();
+		Vector3 max = m.getTranslation();
+		for(UINT32 i = 0; i < 3; i++)
+		{
+			for(UINT32 j = 0; j < 3; j++)
+			{
+				float e = m[i][j] * mMinimum[j];
+				float f = m[i][j] * mMaximum[j];
 
-		Vector3 newCentre = m.multiplyAffine(centre);
-		Vector3 newHalfSize(
-			Math::abs(m[0][0]) * halfSize.x + Math::abs(m[0][1]) * halfSize.y + Math::abs(m[0][2]) * halfSize.z, 
-			Math::abs(m[1][0]) * halfSize.x + Math::abs(m[1][1]) * halfSize.y + Math::abs(m[1][2]) * halfSize.z,
-			Math::abs(m[2][0]) * halfSize.x + Math::abs(m[2][1]) * halfSize.y + Math::abs(m[2][2]) * halfSize.z);
+				if(e < f)
+				{
+					min[i] += e;
+					max[i] += f;
+				}
+				else
+				{
+					min[i] += f;
+					max[i] += e;
+				}
+			}
+			
+		}
 
-		setExtents(newCentre - newHalfSize, newCentre + newHalfSize);
+		setExtents(min, max);
 	}
 
 	bool AABox::intersects(const AABox& b2) const

+ 156 - 153
Source/BansheeUtility/Math/BsMatrix3.h

@@ -11,9 +11,12 @@ namespace bs
 	 *  @{
 	 */
 
-    /** A 3x3 matrix. Can be used for non-homogenous transformations of three dimensional vectors and points. */
-    class BS_UTILITY_EXPORT Matrix3
-    {
+	/** 
+	 * A 3x3 matrix. Can be used for non-homogenous transformations of three dimensional vectors and points. In row major
+	 * format. 
+	 */
+	class BS_UTILITY_EXPORT Matrix3
+	{
 	private:
 		struct EulerAngleOrderData
 		{
@@ -21,7 +24,7 @@ namespace bs
 			float sign;
 		};
 
-    public:
+	public:
 		Matrix3() {}
 
 		Matrix3(BS_ZERO zero)
@@ -32,14 +35,14 @@ namespace bs
 			:Matrix3(Matrix3::IDENTITY)
 		{ }
 
-        Matrix3(const Matrix3& mat)
+		Matrix3(const Matrix3& mat)
 		{
 			memcpy(m, mat.m, 9*sizeof(float));
 		}
 
-        Matrix3(float m00, float m01, float m02,
-                float m10, float m11, float m12,
-                float m20, float m21, float m22)
+		Matrix3(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;
@@ -53,50 +56,50 @@ namespace bs
 		}
 
 		/** Construct a matrix from a quaternion. */
-        explicit Matrix3(const Quaternion& rotation)
-        {
-            fromQuaternion(rotation);
-        }
+		explicit Matrix3(const Quaternion& rotation)
+		{
+			fromQuaternion(rotation);
+		}
 
 		/** Construct a matrix that performs rotation and scale. */
-        explicit Matrix3(const Quaternion& rotation, const Vector3& scale)
-        {
-            fromQuaternion(rotation);
+		explicit Matrix3(const Quaternion& rotation, const Vector3& scale)
+		{
+			fromQuaternion(rotation);
 			
 			for (int row = 0; row < 3; row++)
 			{
 				for (int col = 0; col < 3; col++)
 					m[row][col] = scale[row]*m[row][col];
 			}
-        }
+		}
 
 		/** Construct a matrix from an angle/axis pair. */
-        explicit Matrix3(const Vector3& axis, const Radian& angle)
-        {
-            fromAxisAngle(axis, angle);
-        }
-
-        /** Construct a matrix from 3 orthonormal local axes. */
-        explicit Matrix3(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
-        {
-            fromAxes(xaxis, yaxis, zaxis);
-        }
-
-        /** 
+		explicit Matrix3(const Vector3& axis, const Radian& angle)
+		{
+			fromAxisAngle(axis, angle);
+		}
+
+		/** Construct a matrix from 3 orthonormal local axes. */
+		explicit Matrix3(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
+		{
+			fromAxes(xaxis, yaxis, zaxis);
+		}
+
+		/** 
 		 * Construct a matrix from euler angles, YXZ ordering.
-         * 			
+		 * 			
 		 * @see		Matrix3::fromEulerAngles
-         */
+		 */
 		explicit Matrix3(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
 		{
 			fromEulerAngles(xAngle, yAngle, zAngle);
 		}
 
-        /**
-         * Construct a matrix from euler angles, custom ordering.
-         * 			
+		/**
+		 * Construct a matrix from euler angles, custom ordering.
+		 * 			
 		 * @see		Matrix3::fromEulerAngles
-         */
+		 */
 		explicit Matrix3(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
 		{
 			fromEulerAngles(xAngle, yAngle, zAngle, order);
@@ -116,29 +119,29 @@ namespace bs
 			std::swap(m[2][2], other.m[2][2]);
 		}
 
-        /** Returns a row of the matrix. */
-        float* operator[] (UINT32 row) const
+		/** Returns a row of the matrix. */
+		float* operator[] (UINT32 row) const
 		{
 			assert(row < 3);
 
 			return (float*)m[row];
 		}
 
-        Vector3 getColumn(UINT32 col) const;
-        void setColumn(UINT32 col, const Vector3& vec);
+		Vector3 getColumn(UINT32 col) const;
+		void setColumn(UINT32 col, const Vector3& vec);
 
-        Matrix3& operator= (const Matrix3& rhs)
+		Matrix3& operator= (const Matrix3& rhs)
 		{
 			memcpy(m, rhs.m, 9*sizeof(float));
 			return *this;
 		}
-        bool operator== (const Matrix3& rhs) const;
-        bool operator!= (const Matrix3& rhs) const;
+		bool operator== (const Matrix3& rhs) const;
+		bool operator!= (const Matrix3& rhs) const;
 
-        Matrix3 operator+ (const Matrix3& rhs) const;
-        Matrix3 operator- (const Matrix3& rhs) const;
-        Matrix3 operator* (const Matrix3& rhs) const;
-        Matrix3 operator- () const;
+		Matrix3 operator+ (const Matrix3& rhs) const;
+		Matrix3 operator- (const Matrix3& rhs) const;
+		Matrix3 operator* (const Matrix3& rhs) const;
+		Matrix3 operator- () const;
 		Matrix3 operator* (float rhs) const;
 
 		friend Matrix3 operator* (float lhs, const Matrix3& rhs);
@@ -146,158 +149,158 @@ namespace bs
 		/** Transforms the given vector by this matrix and returns the newly transformed vector. */
 		Vector3 transform(const Vector3& vec) const;
 
-        /** Returns a transpose of the matrix (switched columns and rows). */
-        Matrix3 transpose () const;
-
-        /**
-         * Calculates an inverse of the matrix if it exists.
-         *
-         * @param[out]	mat			Resulting matrix inverse.
-         * @param[in]	fTolerance 	(optional) Tolerance to use when checking if determinant is zero (or near zero in this case).
-         * 							Zero determinant means inverse doesn't exist.
-         * @return					True if inverse exists, false otherwise.
-         */
-        bool inverse(Matrix3& mat, float fTolerance = 1e-06f) const;
-
-        /**
-         * Calculates an inverse of the matrix if it exists.
-         *
+		/** Returns a transpose of the matrix (switched columns and rows). */
+		Matrix3 transpose () const;
+
+		/**
+		 * Calculates an inverse of the matrix if it exists.
+		 *
+		 * @param[out]	mat			Resulting matrix inverse.
 		 * @param[in]	fTolerance 	(optional) Tolerance to use when checking if determinant is zero (or near zero in this case).
 		 * 							Zero determinant means inverse doesn't exist.
-         *
-         * @return					Resulting matrix inverse if it exists, otherwise a zero matrix.
-         */
-        Matrix3 inverse(float fTolerance = 1e-06f) const;
-
-        /** Calculates the matrix determinant. */
-        float determinant() const;
-
-        /**
-         * Decompose a Matrix3 to rotation and scale.
-         *
-         * @note	
+		 * @return					True if inverse exists, false otherwise.
+		 */
+		bool inverse(Matrix3& mat, float fTolerance = 1e-06f) const;
+
+		/**
+		 * Calculates an inverse of the matrix if it exists.
+		 *
+		 * @param[in]	fTolerance 	(optional) Tolerance to use when checking if determinant is zero (or near zero in this case).
+		 * 							Zero determinant means inverse doesn't exist.
+		 *
+		 * @return					Resulting matrix inverse if it exists, otherwise a zero matrix.
+		 */
+		Matrix3 inverse(float fTolerance = 1e-06f) const;
+
+		/** Calculates the matrix determinant. */
+		float determinant() const;
+
+		/**
+		 * Decompose a Matrix3 to rotation and scale.
+		 *
+		 * @note	
 		 * Matrix must consist only of rotation and uniform scale transformations, otherwise accurate results are not 
 		 * guaranteed. Applying non-uniform scale guarantees rotation portion will not be accurate.
-         */
-        void decomposition(Quaternion& rotation, Vector3& scale) const;
-
-        /**
-         * Decomposes the matrix into various useful values.
-         *
-         * @param[out]	matL	Unitary matrix. Columns form orthonormal bases. If your matrix is affine and
-         * 						doesn't use non-uniform scaling this matrix will be a conjugate transpose of the rotation part of the matrix.
-         * @param[out]	matS	Singular values of the matrix. If your matrix is affine these will be scaling factors of the matrix.
+		 */
+		void decomposition(Quaternion& rotation, Vector3& scale) const;
+
+		/**
+		 * Decomposes the matrix into various useful values.
+		 *
+		 * @param[out]	matL	Unitary matrix. Columns form orthonormal bases. If your matrix is affine and
+		 * 						doesn't use non-uniform scaling this matrix will be a conjugate transpose of the rotation part of the matrix.
+		 * @param[out]	matS	Singular values of the matrix. If your matrix is affine these will be scaling factors of the matrix.
 		 * @param[out]	matR	Unitary matrix. Columns form orthonormal bases. If your matrix is affine and
 		 * 						doesn't use non-uniform scaling this matrix will be the rotation part of the matrix.
-         */
-        void singularValueDecomposition(Matrix3& matL, Vector3& matS, Matrix3& matR) const;
-
-        /**
-         * Decomposes the matrix into a set of values.
-         *
-         * @param[out]	matQ	Columns form orthonormal bases. If your matrix is affine and
-         * 						doesn't use non-uniform scaling this matrix will be the rotation part of the matrix.
-         * @param[out]	vecD	If the matrix is affine these will be scaling factors of the matrix.
+		 */
+		void singularValueDecomposition(Matrix3& matL, Vector3& matS, Matrix3& matR) const;
+
+		/**
+		 * Decomposes the matrix into a set of values.
+		 *
+		 * @param[out]	matQ	Columns form orthonormal bases. If your matrix is affine and
+		 * 						doesn't use non-uniform scaling this matrix will be the rotation part of the matrix.
+		 * @param[out]	vecD	If the matrix is affine these will be scaling factors of the matrix.
 		 * @param[out]	vecU	If the matrix is affine these will be shear factors of the matrix.
-         */
+		 */
 		void QDUDecomposition(Matrix3& matQ, Vector3& vecD, Vector3& vecU) const;
 
-        /** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
-        void orthonormalize();
+		/** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
+		void orthonormalize();
 
-        /**
-         * Converts an orthonormal matrix to axis angle representation.
-         *
-         * @note	Matrix must be orthonormal.
-         */
-        void toAxisAngle(Vector3& axis, Radian& angle) const;
+		/**
+		 * Converts an orthonormal matrix to axis angle representation.
+		 *
+		 * @note	Matrix must be orthonormal.
+		 */
+		void toAxisAngle(Vector3& axis, Radian& angle) const;
 
-        /** Creates a rotation matrix from an axis angle representation. */
-        void fromAxisAngle(const Vector3& axis, const Radian& angle);
+		/** Creates a rotation matrix from an axis angle representation. */
+		void fromAxisAngle(const Vector3& axis, const Radian& angle);
 
-        /**
-         * Converts an orthonormal matrix to quaternion representation.
-         *
-         * @note	Matrix must be orthonormal.
-         */
-        void toQuaternion(Quaternion& quat) const;
+		/**
+		 * Converts an orthonormal matrix to quaternion representation.
+		 *
+		 * @note	Matrix must be orthonormal.
+		 */
+		void toQuaternion(Quaternion& quat) const;
 
-        /** Creates a rotation matrix from a quaternion representation. */
-        void fromQuaternion(const Quaternion& quat);
+		/** Creates a rotation matrix from a quaternion representation. */
+		void fromQuaternion(const Quaternion& quat);
 
-        /** Creates a matrix from a three axes. */
+		/** Creates a matrix from a three axes. */
 		void fromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis);
 
-        /**
-         * Converts an orthonormal matrix to euler angle (pitch/yaw/roll) representation.
-         *
-         * @param[in,out]	xAngle	Rotation about x axis. (AKA Pitch)
-         * @param[in,out]	yAngle  Rotation about y axis. (AKA Yaw)
-         * @param[in,out]	zAngle 	Rotation about z axis. (AKA Roll)
-         * @return					True if unique solution was found, false otherwise.
-         * 			
+		/**
+		 * Converts an orthonormal matrix to euler angle (pitch/yaw/roll) representation.
+		 *
+		 * @param[in,out]	xAngle	Rotation about x axis. (AKA Pitch)
+		 * @param[in,out]	yAngle  Rotation about y axis. (AKA Yaw)
+		 * @param[in,out]	zAngle 	Rotation about z axis. (AKA Roll)
+		 * @return					True if unique solution was found, false otherwise.
+		 * 			
 		 * @note	Matrix must be orthonormal.
-         */
-        bool toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const;
+		 */
+		bool toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const;
 
-        /**
-         * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
-         *
+		/**
+		 * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
+		 *
 		 * @param[in]	xAngle	Rotation about x axis. (AKA Pitch)
 		 * @param[in]	yAngle	Rotation about y axis. (AKA Yaw)
 		 * @param[in]	zAngle	Rotation about z axis. (AKA Roll)
-         *
-         * @note	Matrix must be orthonormal.
+		 *
+		 * @note	Matrix must be orthonormal.
 		 * 			Since different values will be produced depending in which order are the rotations applied, this method assumes
 		 * 			they are applied in YXZ order. If you need a specific order, use the overloaded "fromEulerAngles" method instead.
-         */
-        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle);
+		 */
+		void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle);
 
-        /**
-         * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
-         *
+		/**
+		 * Creates a rotation matrix from the provided Pitch/Yaw/Roll angles.
+		 *
 		 * @param[in]	xAngle	Rotation about x axis. (AKA Pitch)
 		 * @param[in]	yAngle	Rotation about y axis. (AKA Yaw)
 		 * @param[in]	zAngle	Rotation about z axis. (AKA Roll)
 		 * @param[in]	order 	The order in which rotations will be applied. 
 		 *						Different rotations can be created depending on the order.
-         *
-         * @note	Matrix must be orthonormal.
-         */
-        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order);
+		 *
+		 * @note	Matrix must be orthonormal.
+		 */
+		void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order);
 
-        /**
-         * Eigensolver, matrix must be symmetric.
+		/**
+		 * Eigensolver, matrix must be symmetric.
 		 *
 		 * @note	
 		 * Eigenvectors are vectors which when transformed by the matrix, only change in magnitude, but not in direction. 
 		 * Eigenvalue is that magnitude. In other words you will get the same result whether you multiply the vector by the 
 		 * matrix or by its eigenvalue.
-         */
-        void eigenSolveSymmetric(float eigenValues[3], Vector3 eigenVectors[3]) const;
+		 */
+		void eigenSolveSymmetric(float eigenValues[3], Vector3 eigenVectors[3]) const;
 
-        static const float EPSILON;
-        static const Matrix3 ZERO;
-        static const Matrix3 IDENTITY;
+		static const float EPSILON;
+		static const Matrix3 ZERO;
+		static const Matrix3 IDENTITY;
 
-    protected:
+	protected:
 		friend class Matrix4;
 
-        // Support for eigensolver
-        void tridiagonal (float diag[3], float subDiag[3]);
-        bool QLAlgorithm (float diag[3], float subDiag[3]);
+		// Support for eigensolver
+		void tridiagonal (float diag[3], float subDiag[3]);
+		bool QLAlgorithm (float diag[3], float subDiag[3]);
 
-        // Support for singular value decomposition
-        static const float SVD_EPSILON;
-        static const unsigned int SVD_MAX_ITERS;
-        static void bidiagonalize (Matrix3& matA, Matrix3& matL, Matrix3& matR);
-        static void golubKahanStep (Matrix3& matA, Matrix3& matL, Matrix3& matR);
+		// Support for singular value decomposition
+		static const float SVD_EPSILON;
+		static const unsigned int SVD_MAX_ITERS;
+		static void bidiagonalize (Matrix3& matA, Matrix3& matL, Matrix3& matR);
+		static void golubKahanStep (Matrix3& matA, Matrix3& matL, Matrix3& matR);
 
 		// Euler angle conversions
 		static const EulerAngleOrderData EA_LOOKUP[6];
 
-        float m[3][3];
-    };
+		float m[3][3];
+	};
 
 	/** @} */
 

+ 17 - 1
Source/BansheeUtility/Math/BsMatrix4.h

@@ -15,7 +15,7 @@ namespace bs
 	 *  @{
 	 */
 
-	 /** Class representing a 4x4 matrix. */
+	 /** Class representing a 4x4 matrix, in row major format. */
 	class BS_UTILITY_EXPORT Matrix4
 	{
 	private:
@@ -218,6 +218,22 @@ namespace bs
 				rhs*m[3][0], rhs*m[3][1], rhs*m[3][2], rhs*m[3][3]);
 		}
 
+		/** Returns the specified column of the matrix, ignoring the last row. */
+		Vector3 getColumn(UINT32 col) const
+		{
+			assert(col < 4);
+
+			return Vector3(m[0][col], m[1][col], m[2][col]);
+		}
+
+		/** Returns the specified column of the matrix. */
+		Vector4 getColumn4D(UINT32 col) const
+		{
+			assert(col < 4);
+
+			return Vector4(m[0][col], m[1][col], m[2][col], m[3][col]);
+		}
+
 		/** Returns a transpose of the matrix (switched columns and rows). */
 		Matrix4 transpose() const
 		{

+ 12 - 4
Source/BansheeUtility/Math/BsSphere.cpp

@@ -27,11 +27,19 @@ namespace bs
 
 	void Sphere::transform(const Matrix4& matrix)
 	{
-		Vector3 edge = mCenter + Vector3::UNIT_X * mRadius;
-		mCenter = matrix.multiplyAffine(mCenter);
-		edge = matrix.multiplyAffine(edge);
+		Matrix3 rotMatrix;
+
+		float lengthSqrd[3];
+		for(UINT32 i = 0; i < 3; i++)
+		{
+			Vector3 column = rotMatrix.getColumn(i);
+			lengthSqrd[i] = column.dot(column);
+		}
+
+		float maxLengthSqrd = std::max(lengthSqrd[0], std::max(lengthSqrd[1], lengthSqrd[2]));
 
-		mRadius = mCenter.distance(edge);
+		mCenter = matrix.multiplyAffine(mCenter);
+		mRadius *= sqrt(maxLengthSqrd);
 	}
 
 	bool Sphere::contains(const Vector3& v) const