├── LICENSE ├── M3DFrame.h ├── M3DFrustum.h ├── M3DGeometryTransform.h ├── M3DMatrixStack.h ├── M3DTriangleMesh.cpp ├── M3DTriangleMesh.h ├── Math3D.pri ├── README.md ├── math3d.cpp └── math3d.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2023 Richard S. Wright Jr. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /M3DFrame.h: -------------------------------------------------------------------------------- 1 | // Frame.h 2 | // Implementation of the GLFrame Class 3 | // Richard S. Wright Jr. 4 | // Code by Richard S. Wright Jr. 5 | /* Copyright (c) 2005-2023, Richard S. Wright Jr. 6 | // Refactored to M3D Frame for use with Vulkan - June 2021 7 | All rights reserved. 8 | 9 | Redistribution and use in source forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | Redistributions of source code must retain the above copyright notice, this list 13 | of conditions and the following disclaimer. 14 | 15 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 16 | to endorse or promote products derived from this software without specific prior 17 | written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "math3d.h" 31 | 32 | #ifndef _M3D_FRAME_ 33 | #define _M3D_FRAME_ 34 | 35 | // The M3DFrame (OrthonormalFrame) class. Possibly the most useful little piece of 3D graphics 36 | // code for OpenGL immersive environments. 37 | // Richard S. Wright Jr. 38 | class M3DFrame 39 | { 40 | protected: 41 | M3DVector3f vOrigin; // Where am I? 42 | M3DVector3f vForward; // Where am I going? 43 | M3DVector3f vUp; // Which way is up? 44 | 45 | public: 46 | // Default position and orientation. At the origin, looking 47 | // down the positive Z axis (right handed coordinate system). 48 | M3DFrame(void) { Reset(); } 49 | 50 | // Add an easy reset button 51 | inline void Reset(void) { 52 | // At origin 53 | vOrigin[0] = 0.0f; vOrigin[1] = 0.0f; vOrigin[2] = 0.0f; 54 | 55 | // Up is up (+Y) 56 | vUp[0] = 0.0f; vUp[1] = 1.0f; vUp[2] = 0.0f; 57 | 58 | // Forward is -Z (default OpenGL) 59 | vForward[0] = 0.0f; vForward[1] = 0.0f; vForward[2] = -1.0f; 60 | } 61 | 62 | 63 | ///////////////////////////////////////////////////////////// 64 | // Set Location 65 | inline void SetOrigin(const M3DVector3f vPoint) { 66 | m3dCopyVector3(vOrigin, vPoint); } 67 | 68 | inline void SetOrigin(float x, float y, float z) { 69 | vOrigin[0] = x; vOrigin[1] = y; vOrigin[2] = z; } 70 | 71 | inline void GetOrigin(M3DVector3f vPoint) { 72 | m3dCopyVector3(vPoint, vOrigin); } 73 | 74 | inline float GetOriginX(void) { return vOrigin[0]; } 75 | inline float GetOriginY(void) { return vOrigin[1]; } 76 | inline float GetOriginZ(void) { return vOrigin[2]; } 77 | 78 | ///////////////////////////////////////////////////////////// 79 | // Set Forward Direction 80 | inline void SetForwardVector(const M3DVector3f vDirection) { 81 | m3dCopyVector3(vForward, vDirection); } 82 | 83 | inline void SetForwardVector(float x, float y, float z) 84 | { vForward[0] = x; vForward[1] = y; vForward[2] = z; } 85 | 86 | inline void GetForwardVector(M3DVector3f vVector) { m3dCopyVector3(vVector, vForward); } 87 | 88 | ///////////////////////////////////////////////////////////// 89 | // Set Up Direction 90 | inline void SetUpVector(const M3DVector3f vDirection) { 91 | m3dCopyVector3(vUp, vDirection); } 92 | 93 | inline void SetUpVector(float x, float y, float z) 94 | { vUp[0] = x; vUp[1] = y; vUp[2] = z; } 95 | 96 | inline void GetUpVector(M3DVector3f vVector) { m3dCopyVector3(vVector, vUp); } 97 | 98 | 99 | ///////////////////////////////////////////////////////////// 100 | // Get Axes 101 | inline void GetZAxis(M3DVector3f vVector) { GetForwardVector(vVector); } 102 | inline void GetYAxis(M3DVector3f vVector) { GetUpVector(vVector); } 103 | inline void GetXAxis(M3DVector3f vVector) { m3dCrossProduct3(vVector, vUp, vForward); } 104 | 105 | 106 | ///////////////////////////////////////////////////////////// 107 | // Translate along orthonormal axis... world or local 108 | inline void TranslateWorld(float x, float y, float z) 109 | { vOrigin[0] += x; vOrigin[1] += y; vOrigin[2] += z; } 110 | 111 | inline void TranslateLocal(float x, float y, float z) 112 | { MoveForward(z); MoveUp(y); MoveRight(x); } 113 | 114 | 115 | ///////////////////////////////////////////////////////////// 116 | // Move Forward (along Z axis) 117 | inline void MoveForward(float fDelta) 118 | { 119 | // Move along direction of front direction 120 | vOrigin[0] += vForward[0] * fDelta; 121 | vOrigin[1] += vForward[1] * fDelta; 122 | vOrigin[2] += vForward[2] * fDelta; 123 | } 124 | 125 | // Move along Y axis 126 | inline void MoveUp(float fDelta) 127 | { 128 | // Move along direction of up direction 129 | vOrigin[0] += vUp[0] * fDelta; 130 | vOrigin[1] += vUp[1] * fDelta; 131 | vOrigin[2] += vUp[2] * fDelta; 132 | } 133 | 134 | // Move along X axis 135 | inline void MoveRight(float fDelta) 136 | { 137 | // Move along direction of right vector 138 | M3DVector3f vCross; 139 | m3dCrossProduct3(vCross, vUp, vForward); 140 | 141 | vOrigin[0] += vCross[0] * fDelta; 142 | vOrigin[1] += vCross[1] * fDelta; 143 | vOrigin[2] += vCross[2] * fDelta; 144 | } 145 | 146 | 147 | /////////////////////////////////////////////////////////////////////// 148 | // Just assemble the matrix 149 | void GetMatrix(M3DMatrix44f matrix, bool bRotationOnly = false) 150 | { 151 | // Calculate the right side (x) vector, drop it right into the matrix 152 | M3DVector3f vXAxis; 153 | m3dCrossProduct3(vXAxis, vUp, vForward); 154 | 155 | // Set matrix column does not fill in the fourth value... 156 | m3dSetMatrixColumn44(matrix, vXAxis, 0); 157 | matrix[3] = 0.0f; 158 | 159 | // Y Column 160 | m3dSetMatrixColumn44(matrix, vUp, 1); 161 | matrix[7] = 0.0f; 162 | 163 | // Z Column 164 | m3dSetMatrixColumn44(matrix, vForward, 2); 165 | matrix[11] = 0.0f; 166 | 167 | // Translation (already done) 168 | if(bRotationOnly == true) 169 | { 170 | matrix[12] = 0.0f; 171 | matrix[13] = 0.0f; 172 | matrix[14] = 0.0f; 173 | } 174 | else 175 | m3dSetMatrixColumn44(matrix, vOrigin, 3); 176 | 177 | matrix[15] = 1.0f; 178 | } 179 | 180 | 181 | 182 | //////////////////////////////////////////////////////////////////////// 183 | // Assemble the camera matrix 184 | void GetCameraMatrix(M3DMatrix44f m, bool bRotationOnly = false) 185 | { 186 | M3DVector3f x, z; 187 | 188 | // Make rotation matrix 189 | // Z vector is reversed 190 | z[0] = -vForward[0]; 191 | z[1] = -vForward[1]; 192 | z[2] = -vForward[2]; 193 | 194 | // X vector = Y cross Z 195 | m3dCrossProduct3(x, vUp, z); 196 | 197 | // Matrix has no translation information and is 198 | // transposed.... (rows instead of columns) 199 | #define M(row,col) m[col*4+row] 200 | M(0, 0) = x[0]; 201 | M(0, 1) = x[1]; 202 | M(0, 2) = x[2]; 203 | M(0, 3) = 0.0; 204 | M(1, 0) = vUp[0]; 205 | M(1, 1) = vUp[1]; 206 | M(1, 2) = vUp[2]; 207 | M(1, 3) = 0.0; 208 | M(2, 0) = z[0]; 209 | M(2, 1) = z[1]; 210 | M(2, 2) = z[2]; 211 | M(2, 3) = 0.0; 212 | M(3, 0) = 0.0; 213 | M(3, 1) = 0.0; 214 | M(3, 2) = 0.0; 215 | M(3, 3) = 1.0; 216 | #undef M 217 | 218 | 219 | if(bRotationOnly) 220 | return; 221 | 222 | // Apply translation too 223 | M3DMatrix44f trans, M; 224 | m3dTranslationMatrix44(trans, -vOrigin[0], -vOrigin[1], -vOrigin[2]); 225 | 226 | m3dMatrixMultiply44(M, m, trans); 227 | 228 | // Copy result back into m 229 | memcpy(m, M, sizeof(float)*16); 230 | } 231 | 232 | 233 | // Rotate around local Y 234 | void RotateLocalY(float fAngle) 235 | { 236 | M3DMatrix44f rotMat; 237 | 238 | // Just Rotate around the up vector 239 | // Create a rotation matrix around my Up (Y) vector 240 | m3dRotationMatrix44(rotMat, fAngle, 241 | vUp[0], vUp[1], vUp[2]); 242 | 243 | M3DVector3f newVect; 244 | 245 | // Rotate forward pointing vector (inlined 3x3 transform) 246 | newVect[0] = rotMat[0] * vForward[0] + rotMat[4] * vForward[1] + rotMat[8] * vForward[2]; 247 | newVect[1] = rotMat[1] * vForward[0] + rotMat[5] * vForward[1] + rotMat[9] * vForward[2]; 248 | newVect[2] = rotMat[2] * vForward[0] + rotMat[6] * vForward[1] + rotMat[10] * vForward[2]; 249 | m3dCopyVector3(vForward, newVect); 250 | } 251 | 252 | 253 | // Rotate around local Z 254 | void RotateLocalZ(float fAngle) 255 | { 256 | M3DMatrix44f rotMat; 257 | 258 | // Only the up vector needs to be rotated 259 | m3dRotationMatrix44(rotMat, fAngle, 260 | vForward[0], vForward[1], vForward[2]); 261 | 262 | M3DVector3f newVect; 263 | newVect[0] = rotMat[0] * vUp[0] + rotMat[4] * vUp[1] + rotMat[8] * vUp[2]; 264 | newVect[1] = rotMat[1] * vUp[0] + rotMat[5] * vUp[1] + rotMat[9] * vUp[2]; 265 | newVect[2] = rotMat[2] * vUp[0] + rotMat[6] * vUp[1] + rotMat[10] * vUp[2]; 266 | m3dCopyVector3(vUp, newVect); 267 | } 268 | 269 | void RotateLocalX(float fAngle) 270 | { 271 | M3DMatrix33f rotMat; 272 | M3DVector3f localX; 273 | M3DVector3f rotVec; 274 | 275 | // Get the local X axis 276 | m3dCrossProduct3(localX, vUp, vForward); 277 | 278 | // Make a Rotation Matrix 279 | m3dRotationMatrix33(rotMat, fAngle, localX[0], localX[1], localX[2]); 280 | 281 | // Rotate Y, and Z 282 | m3dRotateVector(rotVec, vUp, rotMat); 283 | m3dCopyVector3(vUp, rotVec); 284 | 285 | m3dRotateVector(rotVec, vForward, rotMat); 286 | m3dCopyVector3(vForward, rotVec); 287 | } 288 | 289 | 290 | // Reset axes to make sure they are orthonormal. This should be called on occasion 291 | // if the matrix is long-lived and frequently transformed. 292 | void Normalize(void) 293 | { 294 | M3DVector3f vCross; 295 | 296 | // Calculate cross product of up and forward vectors 297 | m3dCrossProduct3(vCross, vUp, vForward); 298 | 299 | // Use result to recalculate forward vector 300 | m3dCrossProduct3(vForward, vCross, vUp); 301 | 302 | // Also check for unit length... 303 | m3dNormalizeVector3(vUp); 304 | m3dNormalizeVector3(vForward); 305 | } 306 | 307 | 308 | // Rotate in world coordinates... 309 | void RotateWorld(float fAngle, float x, float y, float z) 310 | { 311 | M3DMatrix44f rotMat; 312 | 313 | // Create the Rotation matrix 314 | m3dRotationMatrix44(rotMat, fAngle, x, y, z); 315 | 316 | M3DVector3f newVect; 317 | 318 | // Transform the up axis (inlined 3x3 rotation) 319 | newVect[0] = rotMat[0] * vUp[0] + rotMat[4] * vUp[1] + rotMat[8] * vUp[2]; 320 | newVect[1] = rotMat[1] * vUp[0] + rotMat[5] * vUp[1] + rotMat[9] * vUp[2]; 321 | newVect[2] = rotMat[2] * vUp[0] + rotMat[6] * vUp[1] + rotMat[10] * vUp[2]; 322 | m3dCopyVector3(vUp, newVect); 323 | 324 | // Transform the forward axis 325 | newVect[0] = rotMat[0] * vForward[0] + rotMat[4] * vForward[1] + rotMat[8] * vForward[2]; 326 | newVect[1] = rotMat[1] * vForward[0] + rotMat[5] * vForward[1] + rotMat[9] * vForward[2]; 327 | newVect[2] = rotMat[2] * vForward[0] + rotMat[6] * vForward[1] + rotMat[10] * vForward[2]; 328 | m3dCopyVector3(vForward, newVect); 329 | } 330 | 331 | 332 | // Rotate around a local axis 333 | void RotateLocal(float fAngle, float x, float y, float z) 334 | { 335 | M3DVector3f vWorldVect; 336 | M3DVector3f vLocalVect; 337 | m3dLoadVector3(vLocalVect, x, y, z); 338 | 339 | LocalToWorld(vLocalVect, vWorldVect); 340 | RotateWorld(fAngle, vWorldVect[0], vWorldVect[1], vWorldVect[2]); 341 | } 342 | 343 | 344 | // Convert Coordinate Systems 345 | // This is pretty much, do the transformation represented by the rotation 346 | // and position on the point 347 | // Is it better to stick to the convention that the destination always comes 348 | // first, or use the conventions that "sounds" like the function... 349 | void LocalToWorld(const M3DVector3f vLocal, M3DVector3f vWorld) 350 | { 351 | // Create the rotation matrix based on the vectors 352 | M3DMatrix44f rotMat; 353 | 354 | GetMatrix(rotMat, true); 355 | 356 | // Do the rotation (inline it, and remove 4th column...) 357 | vWorld[0] = rotMat[0] * vLocal[0] + rotMat[4] * vLocal[1] + rotMat[8] * vLocal[2]; 358 | vWorld[1] = rotMat[1] * vLocal[0] + rotMat[5] * vLocal[1] + rotMat[9] * vLocal[2]; 359 | vWorld[2] = rotMat[2] * vLocal[0] + rotMat[6] * vLocal[1] + rotMat[10] * vLocal[2]; 360 | 361 | // Translate the point 362 | vWorld[0] += vOrigin[0]; 363 | vWorld[1] += vOrigin[1]; 364 | vWorld[2] += vOrigin[2]; 365 | } 366 | 367 | // Change world coordinates into "local" coordinates 368 | void WorldToLocal(const M3DVector3f vWorld, M3DVector3f vLocal) 369 | { 370 | //////////////////////////////////////////////// 371 | // Translate the origin 372 | M3DVector3f vNewWorld; 373 | vNewWorld[0] = vWorld[0] - vOrigin[0]; 374 | vNewWorld[1] = vWorld[1] - vOrigin[1]; 375 | vNewWorld[2] = vWorld[2] - vOrigin[2]; 376 | 377 | // Create the rotation matrix based on the vectors 378 | M3DMatrix44f rotMat; 379 | M3DMatrix44f invMat; 380 | GetMatrix(rotMat, true); 381 | 382 | // Do the rotation based on inverted matrix 383 | m3dInvertMatrix44(invMat, rotMat); 384 | 385 | vLocal[0] = invMat[0] * vNewWorld[0] + invMat[4] * vNewWorld[1] + invMat[8] * vNewWorld[2]; 386 | vLocal[1] = invMat[1] * vNewWorld[0] + invMat[5] * vNewWorld[1] + invMat[9] * vNewWorld[2]; 387 | vLocal[2] = invMat[2] * vNewWorld[0] + invMat[6] * vNewWorld[1] + invMat[10] * vNewWorld[2]; 388 | } 389 | 390 | ///////////////////////////////////////////////////////////////////////////// 391 | // Transform a point by frame matrix 392 | void TransformPoint(M3DVector3f vPointSrc, M3DVector3f vPointDst) 393 | { 394 | M3DMatrix44f m; 395 | GetMatrix(m, false); // Rotate and translate 396 | vPointDst[0] = m[0] * vPointSrc[0] + m[4] * vPointSrc[1] + m[8] * vPointSrc[2] + m[12];// * v[3]; 397 | vPointDst[1] = m[1] * vPointSrc[0] + m[5] * vPointSrc[1] + m[9] * vPointSrc[2] + m[13];// * v[3]; 398 | vPointDst[2] = m[2] * vPointSrc[0] + m[6] * vPointSrc[1] + m[10] * vPointSrc[2] + m[14];// * v[3]; 399 | } 400 | 401 | //////////////////////////////////////////////////////////////////////////// 402 | // Rotate a vector by frame matrix 403 | void RotateVector(M3DVector3f vVectorSrc, M3DVector3f vVectorDst) 404 | { 405 | M3DMatrix44f m; 406 | GetMatrix(m, true); // Rotate only 407 | 408 | vVectorDst[0] = m[0] * vVectorSrc[0] + m[4] * vVectorSrc[1] + m[8] * vVectorSrc[2]; 409 | vVectorDst[1] = m[1] * vVectorSrc[0] + m[5] * vVectorSrc[1] + m[9] * vVectorSrc[2]; 410 | vVectorDst[2] = m[2] * vVectorSrc[0] + m[6] * vVectorSrc[1] + m[10] * vVectorSrc[2]; 411 | } 412 | }; 413 | 414 | 415 | #endif 416 | -------------------------------------------------------------------------------- /M3DFrustum.h: -------------------------------------------------------------------------------- 1 | // GLFrustum.h 2 | // Code by Richard S. Wright Jr. 3 | // Encapsulates a frustum... works in conjunction 4 | // with M3DFrame 5 | /* Copyright (c) 2005-2023, Richard S. Wright Jr. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | Redistributions of source code must retain the above copyright notice, this list 12 | of conditions and the following disclaimer. 13 | 14 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 15 | to endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 21 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | #include "math3d.h" 29 | #include "M3DFrame.h" 30 | 31 | #ifndef __M3D_FRUSTUM_CLASS 32 | #define __M3D_FRUSTUM_CLASS 33 | 34 | 35 | /////////////////////////////////////////////////////////////////////////////// 36 | /////////////////////////////////////////////////////////////////////////////// 37 | /////////////////////////////////////////////////////////////////////////////// 38 | class M3DFrustum 39 | { 40 | public: 41 | M3DFrustum(void) // Set some Reasonable Defaults 42 | { SetOrthographic(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); } 43 | 44 | // Set the View Frustum 45 | M3DFrustum(float fFov, float fAspect, float fNear, float fFar) 46 | { SetPerspective(fFov, fAspect, fNear, fFar); } 47 | 48 | M3DFrustum(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax) 49 | { SetOrthographic(xMin, xMax, yMin, yMax, zMin, zMax); } 50 | 51 | // Get the projection matrix for this guy 52 | const M3DMatrix44f& GetProjectionMatrix(void) { return projMatrix; } 53 | 54 | // Calculates the corners of the Frustum and sets the projection matrix. 55 | // Orthographics Matrix Projection 56 | void SetOrthographic(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax) 57 | { 58 | m3dMakeOrthographicMatrix(projMatrix, xMin, xMax, yMin, yMax, zMin, zMax); 59 | projMatrix[15] = 1.0f; 60 | 61 | 62 | // Fill in values for untransformed Frustum corners 63 | // Near Upper Left 64 | nearUL[0] = xMin; nearUL[1] = yMax; nearUL[2] = zMin; nearUL[3] = 1.0f; 65 | 66 | // Near Lower Left 67 | nearLL[0] = xMin; nearLL[1] = yMin; nearLL[2] = zMin; nearLL[3] = 1.0f; 68 | 69 | // Near Upper Right 70 | nearUR[0] = xMax; nearUR[1] = yMax; nearUR[2] = zMin; nearUR[3] = 1.0f; 71 | 72 | // Near Lower Right 73 | nearLR[0] = xMax; nearLR[1] = yMin; nearLR[2] = zMin; nearLR[3] = 1.0f; 74 | 75 | // Far Upper Left 76 | farUL[0] = xMin; farUL[1] = yMax; farUL[2] = zMax; farUL[3] = 1.0f; 77 | 78 | // Far Lower Left 79 | farLL[0] = xMin; farLL[1] = yMin; farLL[2] = zMax; farLL[3] = 1.0f; 80 | 81 | // Far Upper Right 82 | farUR[0] = xMax; farUR[1] = yMax; farUR[2] = zMax; farUR[3] = 1.0f; 83 | 84 | // Far Lower Right 85 | farLR[0] = xMax; farLR[1] = yMin; farLR[2] = zMax; farLR[3] = 1.0f; 86 | } 87 | 88 | 89 | // Calculates the corners of the Frustum and sets the projection matrix. 90 | // Perspective Matrix Projection 91 | void SetPerspective(float fFov, float fAspect, float fNear, float fFar) 92 | { 93 | float xmin, xmax, ymin, ymax; // Dimensions of near clipping plane 94 | float xFmin, xFmax, yFmin, yFmax; // Dimensions of far clipping plane 95 | 96 | // Do the Math for the near clipping plane 97 | ymax = fNear * float(tan( fFov * M3D_PI / 360.0 )); 98 | ymin = -ymax; 99 | xmin = ymin * fAspect; 100 | xmax = -xmin; 101 | 102 | // Construct the projection matrix 103 | m3dLoadIdentity44(projMatrix); 104 | projMatrix[0] = (2.0f * fNear)/(xmax - xmin); 105 | projMatrix[5] = (2.0f * fNear)/(ymax - ymin); 106 | projMatrix[8] = (xmax + xmin) / (xmax - xmin); 107 | projMatrix[9] = (ymax + ymin) / (ymax - ymin); 108 | projMatrix[10] = -((fFar + fNear)/(fFar - fNear)); 109 | projMatrix[11] = -1.0f; 110 | projMatrix[14] = -((2.0f * fFar * fNear)/(fFar - fNear)); 111 | projMatrix[15] = 0.0f; 112 | 113 | // Do the Math for the far clipping plane 114 | yFmax = fFar * float(tan(fFov * M3D_PI / 360.0)); 115 | yFmin = -yFmax; 116 | xFmin = yFmin * fAspect; 117 | xFmax = -xFmin; 118 | 119 | 120 | // Fill in values for untransformed Frustum corners 121 | // Near Upper Left 122 | nearUL[0] = xmin; nearUL[1] = ymax; nearUL[2] = -fNear; nearUL[3] = 1.0f; 123 | 124 | // Near Lower Left 125 | nearLL[0] = xmin; nearLL[1] = ymin; nearLL[2] = -fNear; nearLL[3] = 1.0f; 126 | 127 | // Near Upper Right 128 | nearUR[0] = xmax; nearUR[1] = ymax; nearUR[2] = -fNear; nearUR[3] = 1.0f; 129 | 130 | // Near Lower Right 131 | nearLR[0] = xmax; nearLR[1] = ymin; nearLR[2] = -fNear; nearLR[3] = 1.0f; 132 | 133 | // Far Upper Left 134 | farUL[0] = xFmin; farUL[1] = yFmax; farUL[2] = -fFar; farUL[3] = 1.0f; 135 | 136 | // Far Lower Left 137 | farLL[0] = xFmin; farLL[1] = yFmin; farLL[2] = -fFar; farLL[3] = 1.0f; 138 | 139 | // Far Upper Right 140 | farUR[0] = xFmax; farUR[1] = yFmax; farUR[2] = -fFar; farUR[3] = 1.0f; 141 | 142 | // Far Lower Right 143 | farLR[0] = xFmax; farLR[1] = yFmin; farLR[2] = -fFar; farLR[3] = 1.0f; 144 | } 145 | 146 | 147 | // Builds a transformation matrix and transforms the corners of the Frustum, 148 | // then derives the plane equations 149 | void Transform(M3DFrame& Camera) 150 | { 151 | // Workspace 152 | M3DMatrix44f rotMat; 153 | M3DVector3f vForward, vUp, vCross; 154 | M3DVector3f vOrigin; 155 | 156 | /////////////////////////////////////////////////////////////////// 157 | // Create the transformation matrix. This was the trickiest part 158 | // for me. The default view from OpenGL is down the negative Z 159 | // axis. However, building a transformation axis from these 160 | // directional vectors points the frustum the wrong direction. So 161 | // You must reverse them here, or build the initial frustum 162 | // backwards - which to do is purely a matter of taste. I chose to 163 | // compensate here to allow better operability with some of my other 164 | // legacy code and projects. RSW 165 | Camera.GetForwardVector(vForward); 166 | vForward[0] = -vForward[0]; 167 | vForward[1] = -vForward[1]; 168 | vForward[2] = -vForward[2]; 169 | 170 | Camera.GetUpVector(vUp); 171 | Camera.GetOrigin(vOrigin); 172 | 173 | // Calculate the right side (x) vector 174 | m3dCrossProduct3(vCross, vUp, vForward); 175 | 176 | // The Matrix 177 | // X Column 178 | memcpy(rotMat, vCross, sizeof(float)*3); 179 | rotMat[3] = 0.0f; 180 | 181 | // Y Column 182 | memcpy(&rotMat[4], vUp, sizeof(float)*3); 183 | rotMat[7] = 0.0f; 184 | 185 | // Z Column 186 | memcpy(&rotMat[8], vForward, sizeof(float)*3); 187 | rotMat[11] = 0.0f; 188 | 189 | // Translation 190 | rotMat[12] = vOrigin[0]; 191 | rotMat[13] = vOrigin[1]; 192 | rotMat[14] = vOrigin[2]; 193 | rotMat[15] = 1.0f; 194 | 195 | //////////////////////////////////////////////////// 196 | // Transform the frustum corners 197 | m3dTransformVector4(nearULT, nearUL, rotMat); 198 | m3dTransformVector4(nearLLT, nearLL, rotMat); 199 | m3dTransformVector4(nearURT, nearUR, rotMat); 200 | m3dTransformVector4(nearLRT, nearLR, rotMat); 201 | m3dTransformVector4(farULT, farUL, rotMat); 202 | m3dTransformVector4(farLLT, farLL, rotMat); 203 | m3dTransformVector4(farURT, farUR, rotMat); 204 | m3dTransformVector4(farLRT, farLR, rotMat); 205 | 206 | //////////////////////////////////////////////////// 207 | // Derive Plane Equations from points... Points given in 208 | // counter clockwise order to make normals point inside 209 | // the Frustum 210 | // Near and Far Planes 211 | m3dGetPlaneEquation(nearPlane, nearULT, nearLLT, nearLRT); 212 | m3dGetPlaneEquation(farPlane, farULT, farURT, farLRT); 213 | 214 | // Top and Bottom Planes 215 | m3dGetPlaneEquation(topPlane, nearULT, nearURT, farURT); 216 | m3dGetPlaneEquation(bottomPlane, nearLLT, farLLT, farLRT); 217 | 218 | // Left and right planes 219 | m3dGetPlaneEquation(leftPlane, nearLLT, nearULT, farULT); 220 | m3dGetPlaneEquation(rightPlane, nearLRT, farLRT, farURT); 221 | } 222 | 223 | 224 | 225 | // Allow expanded version of sphere test 226 | bool TestSphere(float x, float y, float z, float fRadius) 227 | { 228 | M3DVector3f vPoint; 229 | vPoint[0] = x; 230 | vPoint[1] = y; 231 | vPoint[2] = z; 232 | 233 | return TestSphere(vPoint, fRadius); 234 | } 235 | 236 | // Test a point against all frustum planes. A negative distance for any 237 | // single plane means it is outside the frustum. The radius value allows 238 | // to test for a point (radius = 0), or a sphere. Possibly there might 239 | // be some gain in an alternative function that saves the addition of 240 | // zero in this case. 241 | // Returns false if it is not in the frustum, true if it intersects 242 | // the Frustum. 243 | bool TestSphere(M3DVector3f vPoint, float fRadius) 244 | { 245 | float fDist; 246 | 247 | // Near Plane - See if it is behind me 248 | fDist = m3dGetDistanceToPlane(vPoint, nearPlane); 249 | if(fDist + fRadius <= 0.0) 250 | return false; 251 | 252 | // Distance to far plane 253 | fDist = m3dGetDistanceToPlane(vPoint, farPlane); 254 | if(fDist + fRadius <= 0.0) 255 | return false; 256 | 257 | fDist = m3dGetDistanceToPlane(vPoint, leftPlane); 258 | if(fDist + fRadius <= 0.0) 259 | return false; 260 | 261 | fDist = m3dGetDistanceToPlane(vPoint, rightPlane); 262 | if(fDist + fRadius <= 0.0) 263 | return false; 264 | 265 | fDist = m3dGetDistanceToPlane(vPoint, bottomPlane); 266 | if(fDist + fRadius <= 0.0) 267 | return false; 268 | 269 | fDist = m3dGetDistanceToPlane(vPoint, topPlane); 270 | if(fDist + fRadius <= 0.0) 271 | return false; 272 | 273 | return true; 274 | } 275 | 276 | protected: 277 | // The projection matrix for this frustum 278 | M3DMatrix44f projMatrix; 279 | 280 | // Untransformed corners of the frustum 281 | M3DVector4f nearUL, nearLL, nearUR, nearLR; 282 | M3DVector4f farUL, farLL, farUR, farLR; 283 | 284 | // Transformed corners of Frustum 285 | M3DVector4f nearULT, nearLLT, nearURT, nearLRT; 286 | M3DVector4f farULT, farLLT, farURT, farLRT; 287 | 288 | // Base and Transformed plane equations 289 | M3DVector4f nearPlane, farPlane, leftPlane, rightPlane; 290 | M3DVector4f topPlane, bottomPlane; 291 | }; 292 | 293 | 294 | 295 | #endif 296 | -------------------------------------------------------------------------------- /M3DGeometryTransform.h: -------------------------------------------------------------------------------- 1 | // M3DGeometryTransform 2 | /* 3 | Copyright (c) 2009-2023, Richard S. Wright Jr. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list 10 | of conditions and the following disclaimer. 11 | 12 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 13 | to endorse or promote products derived from this software without specific prior 14 | written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 22 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef __M3D_GEOMETRY_PIPELINE 28 | #define __M3D_GEOMETRY_PIPELINE 29 | 30 | 31 | #include "M3DMatrixStack.h" 32 | 33 | class M3DGeometryTransform 34 | { 35 | public: 36 | M3DGeometryTransform(void) { 37 | _mModelView = NULL; 38 | _mProjection = NULL; 39 | } 40 | 41 | inline void SetModelViewMatrixStack(M3DMatrixStack& mModelView) { _mModelView = &mModelView; } 42 | inline void SetProjectionMatrixStack(M3DMatrixStack& mProjection) { _mProjection = &mProjection; } 43 | 44 | inline M3DMatrixStack& mvStack(void) { return *_mModelView; } 45 | inline M3DMatrixStack& pjStack(void) { return *_mProjection; } 46 | 47 | inline void SetMatrixStacks(M3DMatrixStack& mModelView, M3DMatrixStack& mProjection) { 48 | _mModelView = &mModelView; 49 | _mProjection = &mProjection; 50 | } 51 | 52 | const M3DMatrix44f& GetModelViewProjectionMatrix(void) 53 | { 54 | m3dMatrixMultiply44(_mModelViewProjection, _mProjection->GetMatrix(), _mModelView->GetMatrix()); 55 | return _mModelViewProjection; 56 | } 57 | 58 | inline const M3DMatrix44f& GetModelViewMatrix(void) { return _mModelView->GetMatrix(); } 59 | inline const M3DMatrix44f& GetProjectionMatrix(void) { return _mProjection->GetMatrix(); } 60 | 61 | const M3DMatrix33f& GetNormalMatrix(bool bNormalize = false) 62 | { 63 | m3dExtractRotationMatrix33(_mNormalMatrix, GetModelViewMatrix()); 64 | 65 | if(bNormalize) { 66 | m3dNormalizeVector3(&_mNormalMatrix[0]); 67 | m3dNormalizeVector3(&_mNormalMatrix[3]); 68 | m3dNormalizeVector3(&_mNormalMatrix[6]); 69 | } 70 | 71 | return _mNormalMatrix; 72 | } 73 | 74 | protected: 75 | M3DMatrix44f _mModelViewProjection; 76 | M3DMatrix33f _mNormalMatrix; 77 | 78 | M3DMatrixStack* _mModelView; 79 | M3DMatrixStack* _mProjection; 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /M3DMatrixStack.h: -------------------------------------------------------------------------------- 1 | // GLMatrixStack.h 2 | // Matrix stack functionality 3 | /* 4 | Copyright (c) 2009-2023, Richard S. Wright Jr. 5 | All rights reserved. 6 | 7 | Refactored to M3DMatrixStack for Vulkan - June 2021 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | Redistributions of source code must retain the above copyright notice, this list 13 | of conditions and the following disclaimer. 14 | 15 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 16 | to endorse or promote products derived from this software without specific prior 17 | written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __M3D_MATRIX_STACK 31 | #define __M3D_MATRIX_STACK 32 | 33 | #include "math3d.h" 34 | #include "M3DFrame.h" 35 | 36 | enum M3D_STACK_ERROR { M3D_STACK_NOERROR = 0, M3D_STACK_OVERFLOW, M3D_STACK_UNDERFLOW }; 37 | 38 | class M3DMatrixStack 39 | { 40 | public: 41 | M3DMatrixStack(int iStackDepth = 64) { 42 | stackDepth = iStackDepth; 43 | pStack = new M3DMatrix44f[iStackDepth]; 44 | stackPointer = 0; 45 | m3dLoadIdentity44(pStack[0]); 46 | lastError = M3D_STACK_NOERROR; 47 | } 48 | 49 | 50 | ~M3DMatrixStack(void) { 51 | delete [] pStack; 52 | } 53 | 54 | 55 | inline void LoadIdentity(void) { 56 | m3dLoadIdentity44(pStack[stackPointer]); 57 | } 58 | 59 | inline void LoadMatrix(const M3DMatrix44f mMatrix) { 60 | m3dCopyMatrix44(pStack[stackPointer], mMatrix); 61 | } 62 | 63 | inline void LoadMatrix(M3DFrame& frame) { 64 | M3DMatrix44f m; 65 | frame.GetMatrix(m); 66 | LoadMatrix(m); 67 | } 68 | 69 | inline void MultMatrix(const M3DMatrix44f mMatrix) { 70 | M3DMatrix44f mTemp; 71 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 72 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mMatrix); 73 | } 74 | 75 | inline void MultMatrix(M3DFrame& frame) { 76 | M3DMatrix44f m; 77 | frame.GetMatrix(m); 78 | MultMatrix(m); 79 | } 80 | 81 | inline void PushMatrix(void) { 82 | if(stackPointer < stackDepth-1) { 83 | stackPointer++; 84 | m3dCopyMatrix44(pStack[stackPointer], pStack[stackPointer-1]); 85 | } 86 | else 87 | lastError = M3D_STACK_OVERFLOW; 88 | } 89 | 90 | inline void PopMatrix(void) { 91 | if(stackPointer > 0) 92 | stackPointer--; 93 | else 94 | lastError = M3D_STACK_UNDERFLOW; 95 | } 96 | 97 | void Scale(float x, float y, float z) { 98 | M3DMatrix44f mTemp, mScale; 99 | m3dScaleMatrix44(mScale, x, y, z); 100 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 101 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mScale); 102 | } 103 | 104 | 105 | void Translate(float x, float y, float z) { 106 | M3DMatrix44f mTemp, mScale; 107 | m3dTranslationMatrix44(mScale, x, y, z); 108 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 109 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mScale); 110 | } 111 | 112 | void Rotate(float angle, float x, float y, float z) { 113 | M3DMatrix44f mTemp, mRotate; 114 | m3dRotationMatrix44(mRotate, float(m3dDegToRad(angle)), x, y, z); 115 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 116 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mRotate); 117 | } 118 | 119 | 120 | // I've always wanted vector versions of these 121 | void Scalev(const M3DVector3f vScale) { 122 | M3DMatrix44f mTemp, mScale; 123 | m3dScaleMatrix44(mScale, vScale); 124 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 125 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mScale); 126 | } 127 | 128 | 129 | void Translatev(const M3DVector3f vTranslate) { 130 | M3DMatrix44f mTemp, mTranslate; 131 | m3dLoadIdentity44(mTranslate); 132 | memcpy(&mTranslate[12], vTranslate, sizeof(float) * 3); 133 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 134 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mTranslate); 135 | } 136 | 137 | 138 | void Rotatev(float angle, M3DVector3f vAxis) { 139 | M3DMatrix44f mTemp, mRotation; 140 | m3dRotationMatrix44(mRotation, float(m3dDegToRad(angle)), vAxis[0], vAxis[1], vAxis[2]); 141 | m3dCopyMatrix44(mTemp, pStack[stackPointer]); 142 | m3dMatrixMultiply44(pStack[stackPointer], mTemp, mRotation); 143 | } 144 | 145 | 146 | // I've also always wanted to be able to do this 147 | void PushMatrix(const M3DMatrix44f mMatrix) { 148 | if(stackPointer < stackDepth) { 149 | stackPointer++; 150 | m3dCopyMatrix44(pStack[stackPointer], mMatrix); 151 | } 152 | else 153 | lastError = M3D_STACK_OVERFLOW; 154 | } 155 | 156 | void PushMatrix(M3DFrame& frame) { 157 | M3DMatrix44f m; 158 | frame.GetMatrix(m); 159 | PushMatrix(m); 160 | } 161 | 162 | // Two different ways to get the matrix 163 | const M3DMatrix44f& GetMatrix(void) { return pStack[stackPointer]; } 164 | void GetMatrix(M3DMatrix44f mMatrix) { m3dCopyMatrix44(mMatrix, pStack[stackPointer]); } 165 | 166 | 167 | inline M3D_STACK_ERROR GetLastError(void) { 168 | M3D_STACK_ERROR retval = lastError; 169 | lastError = M3D_STACK_NOERROR; 170 | return retval; 171 | } 172 | 173 | protected: 174 | M3D_STACK_ERROR lastError; 175 | int stackDepth; 176 | int stackPointer; 177 | M3DMatrix44f *pStack; 178 | }; 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /M3DTriangleMesh.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * M3DTriangleMesh.cpp 3 | * 4 | 5 | Copyright (c) 2007-2023, Richard S. Wright Jr. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | Redistributions of source code must retain the above copyright notice, this list 12 | of conditions and the following disclaimer. 13 | 14 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 15 | to endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 21 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | * This class allows you to simply add triangles as if this class were a 30 | * container. The AddTriangle() function searches the current list of triangles 31 | * and determines if the vertex/normal/texcoord is a duplicate. If so, it addes 32 | * an entry to the index array instead of the list of vertices. 33 | * When finished, call EndMesh() to free up extra unneeded memory that is reserved 34 | * as workspace when you call BeginMesh(). 35 | * 36 | * This class can easily be extended to contain other vertex attributes, and to 37 | * save itself and load itself from disk (thus forming the beginnings of a custom 38 | * model file format). 39 | * 40 | * This class was refactored from GLTriangleBatch to just generate geometry, and to 41 | * format it as an interleaved array that could be used by either OpenGL or Vulkan. 42 | * 43 | */ 44 | 45 | #include "M3DTriangleMesh.h" 46 | 47 | 48 | /////////////////////////////////////////////////////////// 49 | // Constructor, does what constructors do... set everything to zero or NULL 50 | M3DTriangleMesh::M3DTriangleMesh(void) 51 | { 52 | pIndexes = NULL; 53 | pVerts = NULL; 54 | pNorms = NULL; 55 | pTexCoords = NULL; 56 | 57 | nMaxIndexes = 0; 58 | nNumIndexes = 0; 59 | nNumVerts = 0; 60 | 61 | bMadeStuff = false; 62 | boundingSphereRadius = 0.0f; 63 | } 64 | 65 | //////////////////////////////////////////////////////////// 66 | // Free any dynamically allocated memory. For those C programmers 67 | // coming to C++, it is perfectly valid to delete a NULL pointer. 68 | M3DTriangleMesh::~M3DTriangleMesh(void) 69 | { 70 | // Just in case these still are allocated when the object is destroyed 71 | delete [] pIndexes; 72 | delete [] pVerts; 73 | delete [] pNorms; 74 | delete [] pTexCoords; 75 | } 76 | 77 | //////////////////////////////////////////////////////////// 78 | // Start assembling a mesh. You need to specify a maximum amount 79 | // of indexes that you expect. The EndMesh will clean up any uneeded 80 | // memory. This is far better than shreading your heap with STL containers... 81 | // At least that's my humble opinion. 82 | void M3DTriangleMesh::BeginMesh(uint32_t nMaxVerts) 83 | { 84 | // Just in case this gets called more than once... 85 | delete [] pIndexes; 86 | delete [] pVerts; 87 | delete [] pNorms; 88 | delete [] pTexCoords; 89 | 90 | nMaxIndexes = nMaxVerts; 91 | nNumIndexes = 0; 92 | nNumVerts = 0; 93 | 94 | // Allocate new blocks. In reality, the other arrays will be 95 | // much shorter than the index array 96 | pIndexes = new uint16_t[nMaxIndexes]; 97 | pVerts = new M3DVector3f[nMaxIndexes]; 98 | pNorms = new M3DVector3f[nMaxIndexes]; 99 | pTexCoords = new M3DVector2f[nMaxIndexes]; 100 | } 101 | 102 | ///////////////////////////////////////////////////////////////// 103 | // Add a triangle to the mesh. This searches the current list for identical 104 | // (well, almost identical - these are floats you know...) verts. If one is found, it 105 | // is added to the index array. If not, it is added to both the index array and the vertex 106 | // array grows by one as well. 107 | void M3DTriangleMesh::AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector2f vTexCoords[3], float epsilon) 108 | { 109 | // First thing we do is make sure the normals are unit length! 110 | // It's almost always a good idea to work with pre-normalized normals 111 | if(vNorms != NULL) { 112 | m3dNormalizeVector3(vNorms[0]); 113 | m3dNormalizeVector3(vNorms[1]); 114 | m3dNormalizeVector3(vNorms[2]); 115 | } 116 | 117 | 118 | // Search for match - triangle consists of three verts 119 | for(uint32_t iVertex = 0; iVertex < 3; iVertex++) 120 | { 121 | uint32_t iMatch = 0; 122 | for(iMatch = 0; iMatch < nNumVerts; iMatch++) 123 | { 124 | // We have vertexes, texture coordinates, and normals 125 | if(pTexCoords && pNorms && vNorms) { 126 | if(m3dCloseEnough(pVerts[iMatch][0], verts[iVertex][0], epsilon) && 127 | m3dCloseEnough(pVerts[iMatch][1], verts[iVertex][1], epsilon) && 128 | m3dCloseEnough(pVerts[iMatch][2], verts[iVertex][2], epsilon) && 129 | 130 | // AND the Normal is the same... 131 | m3dCloseEnough(pNorms[iMatch][0], vNorms[iVertex][0], epsilon) && 132 | m3dCloseEnough(pNorms[iMatch][1], vNorms[iVertex][1], epsilon) && 133 | m3dCloseEnough(pNorms[iMatch][2], vNorms[iVertex][2], epsilon) && 134 | 135 | // And Texture is the same... 136 | m3dCloseEnough(pTexCoords[iMatch][0], vTexCoords[iVertex][0], epsilon) && 137 | m3dCloseEnough(pTexCoords[iMatch][1], vTexCoords[iVertex][1], epsilon)) 138 | { 139 | // Then add the index only 140 | pIndexes[nNumIndexes] = iMatch; 141 | nNumIndexes++; 142 | break; 143 | } 144 | } 145 | } 146 | 147 | // No match for this vertex, add to end of list 148 | if(iMatch == nNumVerts && nNumVerts < nMaxIndexes && nNumIndexes < nMaxIndexes && vNorms && pNorms && pTexCoords) 149 | { 150 | memcpy(pVerts[nNumVerts], verts[iVertex], sizeof(M3DVector3f)); 151 | memcpy(pNorms[nNumVerts], vNorms[iVertex], sizeof(M3DVector3f)); 152 | memcpy(pTexCoords[nNumVerts], vTexCoords[iVertex], sizeof(M3DVector2f)); 153 | 154 | pIndexes[nNumIndexes] = nNumVerts; 155 | nNumIndexes++; 156 | nNumVerts++; 157 | } 158 | } 159 | } 160 | 161 | 162 | 163 | ////////////////////////////////////////////////////////////////// 164 | // Compact the data. This is a nice utility, but you should really 165 | // save the results of the indexing for future use if the model data 166 | // is static (doesn't change). 167 | void M3DTriangleMesh::End(void) 168 | { 169 | bMadeStuff = true; 170 | 171 | // Find the radius of the smallest sphere that would enclose the model 172 | // This is useful for some things. 173 | boundingSphereRadius = 0.0f; 174 | for(unsigned int i = 0; i < nNumVerts; i++) { 175 | float r = m3dGetVectorLengthSquared3(pVerts[i]); 176 | if(r > boundingSphereRadius) 177 | boundingSphereRadius = r; 178 | } 179 | boundingSphereRadius = sqrt(boundingSphereRadius); 180 | } 181 | 182 | 183 | //////////////////////////////////////////////////////////////////////// 184 | // Save the mesh into the already open file stream 185 | bool M3DTriangleMesh::SaveMesh(FILE *pFile) 186 | { 187 | // Header contains... 188 | fwrite(&nNumIndexes, sizeof(uint32_t), 1, pFile); 189 | fwrite(&nNumVerts, sizeof(uint32_t), 1, pFile); 190 | fwrite(&boundingSphereRadius, sizeof(uint32_t), 1, pFile); 191 | 192 | // printf("Unique Verts: %d\r\nTriangles: %d\r\n\r\n", nNumVerts, nNumIndexes); 193 | 194 | fwrite(pIndexes, sizeof(uint16_t) * nNumIndexes, 1, pFile); 195 | 196 | // Vertex positions 197 | fwrite(pVerts, sizeof(M3DVector3f) * nNumVerts, 1, pFile); 198 | 199 | // Normals, if we have them 200 | if(pNorms) { // Look for original flag 201 | fwrite(pNorms, sizeof(M3DVector3f) * nNumVerts, 1, pFile); 202 | } 203 | 204 | // Texture Coordinates, if we have them 205 | if(pTexCoords) // Look for original flag 206 | fwrite(pTexCoords, sizeof(M3DVector2f) * nNumVerts, 1, pFile); 207 | 208 | return true; 209 | } 210 | 211 | 212 | //////////////////////////////////////////////////////////////////////////////////////////// 213 | // Load a mesh into this batch, given the existing and already opened file stream. 214 | // So, You must have verts, but normals and vetexes are optional. You need to know 215 | // ahead of time which are in the file. 216 | bool M3DTriangleMesh::LoadMesh(FILE *pFile, bool bNormals, bool bTexCoords) 217 | { 218 | // Read it all in 219 | fread(&nNumIndexes, sizeof(uint32_t), 1, pFile); 220 | fread(&nNumVerts, sizeof(uint32_t), 1, pFile); 221 | fread(&boundingSphereRadius, sizeof(uint32_t), 1, pFile); 222 | 223 | 224 | pIndexes = new uint16_t[nNumIndexes]; 225 | fread(pIndexes, sizeof(uint16_t) * nNumIndexes, 1, pFile); 226 | 227 | pVerts = new M3DVector3f[nNumVerts]; 228 | fread(pVerts, sizeof(M3DVector3f) * nNumVerts, 1, pFile); 229 | 230 | // Read Normals? If we have them, they occur before the texture coordinates 231 | if(bNormals) { 232 | pNorms = new M3DVector3f[nNumVerts]; 233 | if(1 != fread(pNorms, sizeof(M3DVector3f) * nNumVerts, 1, pFile)) 234 | { // dodo head, no normals 235 | delete [] pNorms; 236 | pNorms = NULL; 237 | } 238 | } 239 | 240 | // These are last, so when loading individual meshes from binary, it's okay if we 241 | // just run out of room. However, for multiple meshes in a single file, we need to 242 | // know if the mesh has texture coordinates or not. CAD models do not have texture 243 | // coordinates. 244 | if(bTexCoords) { 245 | pTexCoords = new M3DVector2f[nNumVerts]; 246 | if(1 != fread(pTexCoords, sizeof(M3DVector2f) * nNumVerts, 1, pFile)) 247 | { // Sorry, no texture coordinates 248 | delete [] pTexCoords; 249 | pTexCoords = NULL; 250 | } 251 | } 252 | 253 | return true; 254 | } 255 | 256 | ////////////////////////////////////////////////////////////////// 257 | // Large meshes can take some time to build, saving and retreiving 258 | // them is a must have. 259 | bool M3DTriangleMesh::SaveMesh(const char *szFileName) 260 | { 261 | FILE *pFile; 262 | pFile = fopen(szFileName, "wb"); 263 | if(pFile == NULL) 264 | return false; 265 | 266 | SaveMesh(pFile); 267 | 268 | 269 | fclose(pFile); 270 | return true; 271 | } 272 | 273 | 274 | ////////////////////////////////////////////////////////////////// 275 | // Large meshes can take some time to build, saving and retreiving 276 | // them is a must have. 277 | bool M3DTriangleMesh::LoadMesh(const char *szFileName, bool bNormals, bool bTexCoords) 278 | { 279 | FILE *pFile = fopen(szFileName, "rb"); 280 | if(pFile == NULL) 281 | return false; 282 | 283 | LoadMesh(pFile, bNormals, bTexCoords); 284 | 285 | fclose(pFile); 286 | 287 | return true; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /M3DTriangleMesh.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TriangleMesh.h 3 | * Refactored originally from The OpenGL SuperBible 4 | * 5 | Copyright (c) 2007-2009-2023, Richard S. Wright Jr. 6 | All rights reserved. 7 | 8 | Redistribution and use in source forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | Redistributions of source code must retain the above copyright notice, this list 12 | of conditions and the following disclaimer. 13 | 14 | Neither the name of Richard S. Wright Jr. nor the names of other contributors may be used 15 | to endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 21 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | * This class allows you to simply add triangles as if this class were a 29 | * container. The AddTriangle() function searches the current list of triangles 30 | * and determines if the vertex/normal/texcoord is a duplicate. If so, it addes 31 | * an entry to the index array instead of the list of vertices. 32 | * When finished, call EndMesh() to free up extra unneeded memory that is reserved 33 | * as workspace when you call BeginMesh(). 34 | * 35 | * This class can easily be extended to contain other vertex attributes, and to 36 | * save itself and load itself from disk (thus forming the beginnings of a custom 37 | * model file format). 38 | * 39 | */ 40 | 41 | #ifndef __TRIANGLE_MESH 42 | #define __TRIANGLE_MESH 43 | 44 | 45 | #include "math3d.h" 46 | #include 47 | #include 48 | 49 | 50 | class M3DTriangleMesh 51 | { 52 | public: 53 | M3DTriangleMesh(void); 54 | virtual ~M3DTriangleMesh(void); 55 | 56 | // Use these three functions to add triangles 57 | void BeginMesh(uint32_t nMaxVerts); 58 | void AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector2f vTexCoords[3], float epsilon = 0.0000001f); 59 | void End(void); 60 | 61 | // Useful for statistics 62 | inline uint32_t GetIndexCount(void) { return nNumIndexes; } 63 | inline uint32_t GetVertexCount(void) { return nNumVerts; } 64 | 65 | const M3DVector3f* getVertexPositions(void) { return pVerts; } 66 | const M3DVector3f* getVertexNormals(void) { return pNorms; } 67 | const M3DVector2f* getVertexTexCoords(void) { return pTexCoords; } 68 | const uint16_t* getVertexIndicies(void) { return pIndexes; } 69 | 70 | inline float GetBoundingSphere(void) { return boundingSphereRadius; } 71 | 72 | // Save to a single file 73 | bool SaveMesh(const char *szFileName); 74 | bool LoadMesh(const char *szFileName, bool bNormals = true, bool bTexCoords = true); 75 | 76 | // Stream to an existing file 77 | bool SaveMesh(FILE *pFile); 78 | bool LoadMesh(FILE *pFile, bool bNormals = true, bool bTexCoords = true); 79 | 80 | protected: 81 | uint16_t *pIndexes; // Array of indexes 82 | M3DVector3f *pVerts; // Array of vertices 83 | M3DVector3f *pNorms; // Array of normals 84 | M3DVector2f *pTexCoords; // Array of texture coordinates 85 | 86 | uint32_t nMaxIndexes; // Maximum workspace 87 | uint32_t nNumIndexes; // Number of indexes currently used 88 | uint32_t nNumVerts; // Number of vertices actually used 89 | 90 | bool bMadeStuff; 91 | 92 | float boundingSphereRadius; 93 | }; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /Math3D.pri: -------------------------------------------------------------------------------- 1 | #3D Math Library 2 | 3 | INCLUDEPATH += $$PWD 4 | 5 | 6 | HEADERS += $$PWD/math3d.h \ 7 | $$PWD/M3DFrame.h \ 8 | $$PWD/M3DFrustum.h \ 9 | $$PWD/M3DGeometryTransform.h \ 10 | $$PWD/M3DMatrixStack.h \ 11 | $$PWD/M3DTriangleMesh.h 12 | 13 | 14 | SOURCES += $$PWD/math3d.cpp \ 15 | $$PWD/M3DTriangleMesh.cpp 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Math3D 2 | A simple vector and matrix library containing functions useful for 3D graphics programming. 3 | -------------------------------------------------------------------------------- /math3d.cpp: -------------------------------------------------------------------------------- 1 | // math3d.cpp 2 | // Math3D Library 3 | 4 | /* Copyright (c) 2007-2023, Richard S. Wright Jr. 5 | MIT License 6 | 7 | Copyright (c) 2007-2023 Richard S. Wright Jr. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the source coce. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | Contact GitHub API Training Shop Blog About 27 | 28 | */ 29 | 30 | // Implementation file for the Math3d library. The C-Runtime has math.h, these routines 31 | // are meant to suppliment math.h by adding geometry/math routines 32 | // useful for graphics, simulation, and physics applications (3D stuff). 33 | // This library is meant to be useful on Win32, Mac OS X, various Linux/Unix distros, 34 | // and mobile platforms. Although designed with OpenGL in mind, there are no OpenGL 35 | // dependencies. Other than standard math routines, the only other outside routine 36 | // used is memcpy (for faster copying of vector arrays). 37 | // Most of the library is inlined. Some functions however are here as I judged them 38 | // too big to be inlined all over the place... nothing prevents anyone from changing 39 | // this if it better fits their project requirements. 40 | // Richard S. Wright Jr. 41 | 42 | // Most functions are in-lined... and are defined here 43 | #include "math3d.h" 44 | 45 | ///////////////////////////////////////////////////////////////////////////////////////////////////// 46 | // Rotates an XY coordiante around the origin. Rotation angle is in degrees for convienience. Positive angles rotate 47 | // counterclockwise around the origin. Negative angles rotate clockwise. 48 | void m3dPerform2DRotationOnPoint(const double inXY[2], double outXY[2], double rotDegrees) 49 | { 50 | M3DMatrix44d mRot; 51 | double rotRadians = m3dRadToDeg(rotDegrees); 52 | m3dRotationMatrix44(mRot, rotRadians, 0.0, 0.0, 1.0); 53 | M3DVector3d vIn, vOut; 54 | 55 | vIn[0] = inXY[0]; 56 | vIn[1] = inXY[1]; 57 | vIn[2] = 0.0; 58 | 59 | m3dTransformVector3(vOut, vIn, mRot); 60 | outXY[0] = vOut[0]; 61 | outXY[1] = vOut[1]; 62 | } 63 | 64 | 65 | //////////////////////////////////////////////////////////// 66 | // LoadIdentity 67 | // For 3x3 and 4x4 float and double matricies. 68 | // 3x3 float 69 | void m3dLoadIdentity33(M3DMatrix33f m) 70 | { 71 | // Don't be fooled, this is still column major 72 | static M3DMatrix33f identity = { 1.0f, 0.0f, 0.0f , 73 | 0.0f, 1.0f, 0.0f, 74 | 0.0f, 0.0f, 1.0f }; 75 | 76 | memcpy(m, identity, sizeof(M3DMatrix33f)); 77 | } 78 | 79 | // 3x3 double 80 | void m3dLoadIdentity33(M3DMatrix33d m) 81 | { 82 | // Don't be fooled, this is still column major 83 | static M3DMatrix33d identity = { 1.0, 0.0, 0.0 , 84 | 0.0, 1.0, 0.0, 85 | 0.0, 0.0, 1.0 }; 86 | 87 | memcpy(m, identity, sizeof(M3DMatrix33d)); 88 | } 89 | 90 | // 4x4 float 91 | void m3dLoadIdentity44(M3DMatrix44f m) 92 | { 93 | // Don't be fooled, this is still column major 94 | static M3DMatrix44f identity = { 1.0f, 0.0f, 0.0f, 0.0f, 95 | 0.0f, 1.0f, 0.0f, 0.0f, 96 | 0.0f, 0.0f, 1.0f, 0.0f, 97 | 0.0f, 0.0f, 0.0f, 1.0f }; 98 | 99 | memcpy(m, identity, sizeof(M3DMatrix44f)); 100 | } 101 | 102 | // 4x4 double 103 | void m3dLoadIdentity44(M3DMatrix44d m) 104 | { 105 | static M3DMatrix44d identity = { 1.0, 0.0, 0.0, 0.0, 106 | 0.0, 1.0, 0.0, 0.0, 107 | 0.0, 0.0, 1.0, 0.0, 108 | 0.0, 0.0, 0.0, 1.0 }; 109 | 110 | memcpy(m, identity, sizeof(M3DMatrix44d)); 111 | } 112 | 113 | 114 | //////////////////////////////////////////////////////////////////////// 115 | // Return the square of the distance between two points 116 | // Should these be inlined...? 117 | float m3dGetDistanceSquared3(const M3DVector3f u, const M3DVector3f v) 118 | { 119 | float x = u[0] - v[0]; 120 | x = x*x; 121 | 122 | float y = u[1] - v[1]; 123 | y = y*y; 124 | 125 | float z = u[2] - v[2]; 126 | z = z*z; 127 | 128 | return (x + y + z); 129 | } 130 | 131 | // Ditto above, but for doubles 132 | double m3dGetDistanceSquared3(const M3DVector3d u, const M3DVector3d v) 133 | { 134 | double x = u[0] - v[0]; 135 | x = x*x; 136 | 137 | double y = u[1] - v[1]; 138 | y = y*y; 139 | 140 | double z = u[2] - v[2]; 141 | z = z*z; 142 | 143 | return (x + y + z); 144 | } 145 | 146 | #define A(row,col) a[(col<<2)+row] 147 | #define B(row,col) b[(col<<2)+row] 148 | #define P(row,col) product[(col<<2)+row] 149 | 150 | /////////////////////////////////////////////////////////////////////////////// 151 | // Multiply two 4x4 matricies 152 | void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b ) 153 | { 154 | for (int i = 0; i < 4; i++) { 155 | float ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); 156 | P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); 157 | P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); 158 | P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); 159 | P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); 160 | } 161 | } 162 | 163 | // Ditto above, but for doubles 164 | void m3dMatrixMultiply44(M3DMatrix44d product, const M3DMatrix44d a, const M3DMatrix44d b ) 165 | { 166 | for (int i = 0; i < 4; i++) { 167 | double ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); 168 | P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); 169 | P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); 170 | P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); 171 | P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); 172 | } 173 | } 174 | #undef A 175 | #undef B 176 | #undef P 177 | 178 | 179 | #define A33(row,col) a[(col*3)+row] 180 | #define B33(row,col) b[(col*3)+row] 181 | #define P33(row,col) product[(col*3)+row] 182 | 183 | /////////////////////////////////////////////////////////////////////////////// 184 | // Multiply two 3x3 matricies 185 | void m3dMatrixMultiply33(M3DMatrix33f product, const M3DMatrix33f a, const M3DMatrix33f b ) 186 | { 187 | for (int i = 0; i < 3; i++) { 188 | float ai0=A33(i,0), ai1=A33(i,1), ai2=A33(i,2); 189 | P33(i,0) = ai0 * B33(0,0) + ai1 * B33(1,0) + ai2 * B33(2,0); 190 | P33(i,1) = ai0 * B33(0,1) + ai1 * B33(1,1) + ai2 * B33(2,1); 191 | P33(i,2) = ai0 * B33(0,2) + ai1 * B33(1,2) + ai2 * B33(2,2); 192 | } 193 | } 194 | 195 | // Ditto above, but for doubles 196 | void m3dMatrixMultiply33(M3DMatrix33d product, const M3DMatrix33d a, const M3DMatrix33d b ) 197 | { 198 | for (int i = 0; i < 3; i++) { 199 | double ai0=A33(i,0), ai1=A33(i,1), ai2=A33(i,2); 200 | P33(i,0) = ai0 * B33(0,0) + ai1 * B33(1,0) + ai2 * B33(2,0); 201 | P33(i,1) = ai0 * B33(0,1) + ai1 * B33(1,1) + ai2 * B33(2,1); 202 | P33(i,2) = ai0 * B33(0,2) + ai1 * B33(1,2) + ai2 * B33(2,2); 203 | } 204 | } 205 | 206 | #undef A33 207 | #undef B33 208 | #undef P33 209 | 210 | 211 | 212 | //////////////////////////////////////////////////////////////////////////////////////////// 213 | // Create a projection matrix 214 | // Similiar to the old gluPerspective... fov is in radians btw... 215 | void m3dMakePerspectiveMatrix(M3DMatrix44f mProjection, float fFov, float fAspect, float zMin, float zMax) 216 | { 217 | m3dLoadIdentity44(mProjection); // Fastest way to get most valid values already in place 218 | 219 | float yMax = zMin * tanf(fFov * 0.5f); 220 | float yMin = -yMax; 221 | float xMin = yMin * fAspect; 222 | float xMax = -xMin; 223 | 224 | mProjection[0] = (2.0f * zMin) / (xMax - xMin); 225 | mProjection[5] = (2.0f * zMin) / (yMax - yMin); 226 | mProjection[8] = (xMax + xMin) / (xMax - xMin); 227 | mProjection[9] = (yMax + yMin) / (yMax - yMin); 228 | mProjection[10] = -((zMax + zMin) / (zMax - zMin)); 229 | mProjection[11] = -1.0f; 230 | mProjection[14] = -((2.0f * (zMax*zMin))/(zMax - zMin)); 231 | mProjection[15] = 0.0f; 232 | } 233 | 234 | /////////////////////////////////////////////////////////////////////////////// 235 | // Make a orthographic projection matrix 236 | void m3dMakeOrthographicMatrix(M3DMatrix44f mProjection, float xMin, float xMax, float yMin, float yMax, float zMin, float zMax) 237 | { 238 | m3dLoadIdentity44(mProjection); 239 | 240 | mProjection[0] = 2.0f / (xMax - xMin); 241 | mProjection[5] = 2.0f / (yMax - yMin); 242 | mProjection[10] = -2.0f / (zMax - zMin); 243 | mProjection[12] = -((xMax + xMin)/(xMax - xMin)); 244 | mProjection[13] = -((yMax + yMin)/(yMax - yMin)); 245 | mProjection[14] = -((zMax + zMin)/(zMax - zMin)); 246 | mProjection[15] = 1.0f; 247 | } 248 | 249 | 250 | 251 | #define M33(row,col) m[col*3+row] 252 | /////////////////////////////////////////////////////////////////////////////// 253 | // Creates a 3x3 rotation matrix, takes radians NOT degrees 254 | void m3dRotationMatrix33(M3DMatrix33f m, float angle, float x, float y, float z) 255 | { 256 | 257 | float mag, s, c; 258 | float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c; 259 | 260 | s = float(sin(angle)); 261 | c = float(cos(angle)); 262 | 263 | mag = float(sqrt( x*x + y*y + z*z )); 264 | 265 | // Identity matrix 266 | if (mag == 0.0f) { 267 | m3dLoadIdentity33(m); 268 | return; 269 | } 270 | 271 | // Rotation matrix is normalized 272 | x /= mag; 273 | y /= mag; 274 | z /= mag; 275 | 276 | xx = x * x; 277 | yy = y * y; 278 | zz = z * z; 279 | xy = x * y; 280 | yz = y * z; 281 | zx = z * x; 282 | xs = x * s; 283 | ys = y * s; 284 | zs = z * s; 285 | one_c = 1.0f - c; 286 | 287 | M33(0,0) = (one_c * xx) + c; 288 | M33(0,1) = (one_c * xy) - zs; 289 | M33(0,2) = (one_c * zx) + ys; 290 | 291 | M33(1,0) = (one_c * xy) + zs; 292 | M33(1,1) = (one_c * yy) + c; 293 | M33(1,2) = (one_c * yz) - xs; 294 | 295 | M33(2,0) = (one_c * zx) - ys; 296 | M33(2,1) = (one_c * yz) + xs; 297 | M33(2,2) = (one_c * zz) + c; 298 | } 299 | 300 | #undef M33 301 | 302 | /////////////////////////////////////////////////////////////////////////////// 303 | // Creates a 4x4 rotation matrix, takes radians NOT degrees 304 | void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z) 305 | { 306 | float mag, s, c; 307 | float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c; 308 | 309 | s = float(sin(angle)); 310 | c = float(cos(angle)); 311 | 312 | mag = float(sqrt( x*x + y*y + z*z )); 313 | 314 | // Identity matrix 315 | if (mag == 0.0f) { 316 | m3dLoadIdentity44(m); 317 | return; 318 | } 319 | 320 | // Rotation matrix is normalized 321 | x /= mag; 322 | y /= mag; 323 | z /= mag; 324 | 325 | #define M(row,col) m[col*4+row] 326 | 327 | xx = x * x; 328 | yy = y * y; 329 | zz = z * z; 330 | xy = x * y; 331 | yz = y * z; 332 | zx = z * x; 333 | xs = x * s; 334 | ys = y * s; 335 | zs = z * s; 336 | one_c = 1.0f - c; 337 | 338 | M(0,0) = (one_c * xx) + c; 339 | M(0,1) = (one_c * xy) - zs; 340 | M(0,2) = (one_c * zx) + ys; 341 | M(0,3) = 0.0f; 342 | 343 | M(1,0) = (one_c * xy) + zs; 344 | M(1,1) = (one_c * yy) + c; 345 | M(1,2) = (one_c * yz) - xs; 346 | M(1,3) = 0.0f; 347 | 348 | M(2,0) = (one_c * zx) - ys; 349 | M(2,1) = (one_c * yz) + xs; 350 | M(2,2) = (one_c * zz) + c; 351 | M(2,3) = 0.0f; 352 | 353 | M(3,0) = 0.0f; 354 | M(3,1) = 0.0f; 355 | M(3,2) = 0.0f; 356 | M(3,3) = 1.0f; 357 | 358 | #undef M 359 | } 360 | 361 | 362 | 363 | /////////////////////////////////////////////////////////////////////////////// 364 | // Ditto above, but for doubles 365 | void m3dRotationMatrix33(M3DMatrix33d m, double angle, double x, double y, double z) 366 | { 367 | double mag, s, c; 368 | double xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c; 369 | 370 | s = sin(angle); 371 | c = cos(angle); 372 | 373 | mag = sqrt( x*x + y*y + z*z ); 374 | 375 | // Identity matrix 376 | if (mag == 0.0) { 377 | m3dLoadIdentity33(m); 378 | return; 379 | } 380 | 381 | // Rotation matrix is normalized 382 | x /= mag; 383 | y /= mag; 384 | z /= mag; 385 | 386 | #define M(row,col) m[col*3+row] 387 | 388 | xx = x * x; 389 | yy = y * y; 390 | zz = z * z; 391 | xy = x * y; 392 | yz = y * z; 393 | zx = z * x; 394 | xs = x * s; 395 | ys = y * s; 396 | zs = z * s; 397 | one_c = 1.0 - c; 398 | 399 | M(0,0) = (one_c * xx) + c; 400 | M(0,1) = (one_c * xy) - zs; 401 | M(0,2) = (one_c * zx) + ys; 402 | 403 | M(1,0) = (one_c * xy) + zs; 404 | M(1,1) = (one_c * yy) + c; 405 | M(1,2) = (one_c * yz) - xs; 406 | 407 | M(2,0) = (one_c * zx) - ys; 408 | M(2,1) = (one_c * yz) + xs; 409 | M(2,2) = (one_c * zz) + c; 410 | 411 | #undef M 412 | } 413 | 414 | 415 | /////////////////////////////////////////////////////////////////////////////// 416 | // Creates a 4x4 rotation matrix, takes radians NOT degrees 417 | void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z) 418 | { 419 | double mag, s, c; 420 | double xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c; 421 | 422 | s = sin(angle); 423 | c = cos(angle); 424 | 425 | mag = sqrt( x*x + y*y + z*z ); 426 | 427 | // Identity matrix 428 | if (mag == 0.0) { 429 | m3dLoadIdentity44(m); 430 | return; 431 | } 432 | 433 | // Rotation matrix is normalized 434 | x /= mag; 435 | y /= mag; 436 | z /= mag; 437 | 438 | #define M(row,col) m[col*4+row] 439 | 440 | xx = x * x; 441 | yy = y * y; 442 | zz = z * z; 443 | xy = x * y; 444 | yz = y * z; 445 | zx = z * x; 446 | xs = x * s; 447 | ys = y * s; 448 | zs = z * s; 449 | one_c = 1.0f - c; 450 | 451 | M(0,0) = (one_c * xx) + c; 452 | M(0,1) = (one_c * xy) - zs; 453 | M(0,2) = (one_c * zx) + ys; 454 | M(0,3) = 0.0; 455 | 456 | M(1,0) = (one_c * xy) + zs; 457 | M(1,1) = (one_c * yy) + c; 458 | M(1,2) = (one_c * yz) - xs; 459 | M(1,3) = 0.0; 460 | 461 | M(2,0) = (one_c * zx) - ys; 462 | M(2,1) = (one_c * yz) + xs; 463 | M(2,2) = (one_c * zz) + c; 464 | M(2,3) = 0.0; 465 | 466 | M(3,0) = 0.0; 467 | M(3,1) = 0.0; 468 | M(3,2) = 0.0; 469 | M(3,3) = 1.0; 470 | 471 | #undef M 472 | } 473 | 474 | //////////////////////////////////////////////////////////////////////////// 475 | /// This function is not exported by library, just for this modules use only 476 | // 3x3 determinant 477 | static float DetIJ(const M3DMatrix44f m, const int i, const int j) 478 | { 479 | int x, y, ii, jj; 480 | float ret, mat[3][3]; 481 | 482 | x = 0; 483 | for (ii = 0; ii < 4; ii++) 484 | { 485 | if (ii == i) continue; 486 | y = 0; 487 | for (jj = 0; jj < 4; jj++) 488 | { 489 | if (jj == j) continue; 490 | mat[x][y] = m[(ii*4)+jj]; 491 | y++; 492 | } 493 | x++; 494 | } 495 | 496 | ret = mat[0][0]*(mat[1][1]*mat[2][2]-mat[2][1]*mat[1][2]); 497 | ret -= mat[0][1]*(mat[1][0]*mat[2][2]-mat[2][0]*mat[1][2]); 498 | ret += mat[0][2]*(mat[1][0]*mat[2][1]-mat[2][0]*mat[1][1]); 499 | 500 | return ret; 501 | } 502 | 503 | 504 | //////////////////////////////////////////////////////////////////////////// 505 | /// This function is not exported by library, just for this modules use only 506 | // 3x3 determinant 507 | static double DetIJ(const M3DMatrix44d m, const int i, const int j) 508 | { 509 | int x, y, ii, jj; 510 | double ret, mat[3][3]; 511 | 512 | x = 0; 513 | for (ii = 0; ii < 4; ii++) 514 | { 515 | if (ii == i) continue; 516 | y = 0; 517 | for (jj = 0; jj < 4; jj++) 518 | { 519 | if (jj == j) continue; 520 | mat[x][y] = m[(ii*4)+jj]; 521 | y++; 522 | } 523 | x++; 524 | } 525 | 526 | ret = mat[0][0]*(mat[1][1]*mat[2][2]-mat[2][1]*mat[1][2]); 527 | ret -= mat[0][1]*(mat[1][0]*mat[2][2]-mat[2][0]*mat[1][2]); 528 | ret += mat[0][2]*(mat[1][0]*mat[2][1]-mat[2][0]*mat[1][1]); 529 | 530 | return ret; 531 | } 532 | 533 | //////////////////////////////////////////////////////////////////////////// 534 | /// 535 | // Invert matrix 536 | void m3dInvertMatrix44(M3DMatrix44f mInverse, const M3DMatrix44f m) 537 | { 538 | int i, j; 539 | float det, detij; 540 | 541 | // calculate 4x4 determinant 542 | det = 0.0f; 543 | for (i = 0; i < 4; i++) 544 | { 545 | det += (i & 0x1) ? (-m[i] * DetIJ(m, 0, i)) : (m[i] * DetIJ(m, 0,i)); 546 | } 547 | det = 1.0f / det; 548 | 549 | // calculate inverse 550 | for (i = 0; i < 4; i++) 551 | { 552 | for (j = 0; j < 4; j++) 553 | { 554 | detij = DetIJ(m, j, i); 555 | mInverse[(i*4)+j] = ((i+j) & 0x1) ? (-detij * det) : (detij *det); 556 | } 557 | } 558 | } 559 | 560 | //////////////////////////////////////////////////////////////////////////// 561 | /// 562 | // Invert matrix 563 | void m3dInvertMatrix44(M3DMatrix44d mInverse, const M3DMatrix44d m) 564 | { 565 | int i, j; 566 | double det, detij; 567 | 568 | // calculate 4x4 determinant 569 | det = 0.0; 570 | for (i = 0; i < 4; i++) 571 | { 572 | det += (i & 0x1) ? (-m[i] * DetIJ(m, 0, i)) : (m[i] * DetIJ(m, 0,i)); 573 | } 574 | det = 1.0 / det; 575 | 576 | // calculate inverse 577 | for (i = 0; i < 4; i++) 578 | { 579 | for (j = 0; j < 4; j++) 580 | { 581 | detij = DetIJ(m, j, i); 582 | mInverse[(i*4)+j] = ((i+j) & 0x1) ? (-detij * det) : (detij *det); 583 | } 584 | } 585 | } 586 | 587 | 588 | /////////////////////////////////////////////////////////////////////////////////////// 589 | // Get Window coordinates, discard Z... 590 | void m3dProjectXY(M3DVector2f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn) 591 | { 592 | M3DVector4f vBack, vForth; 593 | 594 | memcpy(vBack, vPointIn, sizeof(float)*3); 595 | vBack[3] = 1.0f; 596 | 597 | m3dTransformVector4(vForth, vBack, mModelView); 598 | m3dTransformVector4(vBack, vForth, mProjection); 599 | 600 | if(!m3dCloseEnough(vBack[3], 0.0f, 0.000001f)) { 601 | float div = 1.0f / vBack[3]; 602 | vBack[0] *= div; 603 | vBack[1] *= div; 604 | //vBack[2] *= div; 605 | } 606 | 607 | vPointOut[0] = float(iViewPort[0])+(1.0f+float(vBack[0]))*float(iViewPort[2])/2.0f; 608 | vPointOut[1] = float(iViewPort[1])+(1.0f+float(vBack[1]))*float(iViewPort[3])/2.0f; 609 | 610 | // This was put in for Grand Tour... I think it's right. 611 | // .... please report any bugs 612 | if(iViewPort[0] != 0) // Cast to float is expensive... avoid if posssible 613 | vPointOut[0] -= float(iViewPort[0]); 614 | 615 | if(iViewPort[1] != 0) 616 | vPointOut[1] -= float(iViewPort[1]); 617 | } 618 | 619 | /////////////////////////////////////////////////////////////////////////////////////// 620 | // Get window coordinates, we also want Z.... 621 | void m3dProjectXYZ(M3DVector3f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn) 622 | { 623 | M3DVector4f vBack, vForth; 624 | 625 | memcpy(vBack, vPointIn, sizeof(float)*3); 626 | vBack[3] = 1.0f; 627 | 628 | m3dTransformVector4(vForth, vBack, mModelView); 629 | m3dTransformVector4(vBack, vForth, mProjection); 630 | 631 | if(!m3dCloseEnough(vBack[3], 0.0f, 0.000001f)) { 632 | float div = 1.0f / vBack[3]; 633 | vBack[0] *= div; 634 | vBack[1] *= div; 635 | vBack[2] *= div; 636 | } 637 | 638 | vPointOut[0] = float(iViewPort[0])+(1.0f+float(vBack[0]))*float(iViewPort[2])/2.0f; 639 | vPointOut[1] = float(iViewPort[1])+(1.0f+float(vBack[1]))*float(iViewPort[3])/2.0f; 640 | 641 | if(iViewPort[0] != 0) // Cast to float is expensive... avoid if posssible 642 | vPointOut[0] -= float(iViewPort[0]); 643 | 644 | if(iViewPort[1] != 0) 645 | vPointOut[1] -= float(iViewPort[1]); 646 | 647 | vPointOut[2] = vBack[2]; 648 | } 649 | 650 | 651 | 652 | /////////////////////////////////////////////////////////////////////////////// 653 | /////////////////////////////////////////////////////////////////////////////// 654 | // Misc. Utilities 655 | /////////////////////////////////////////////////////////////////////////////// 656 | // Calculates the normal of a triangle specified by the three points 657 | // p1, p2, and p3. Each pointer points to an array of three floats. The 658 | // triangle is assumed to be wound counter clockwise. 659 | void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, 660 | const M3DVector3f point3) 661 | { 662 | M3DVector3f v1,v2; // Temporary vectors 663 | 664 | // Calculate two vectors from the three points. Assumes counter clockwise 665 | // winding! 666 | // v1[0] = point1[0] - point2[0]; 667 | // v1[1] = point1[1] - point2[1]; 668 | // v1[2] = point1[2] - point2[2]; 669 | // 670 | // v2[0] = point2[0] - point3[0]; 671 | // v2[1] = point2[1] - point3[1]; 672 | // v2[2] = point2[2] - point3[2]; 673 | 674 | 675 | v1[0] = point2[0] - point1[0]; 676 | v1[1] = point2[1] - point1[1]; 677 | v1[2] = point2[2] - point1[2]; 678 | 679 | v2[0] = point3[0] - point1[0]; 680 | v2[1] = point3[1] - point1[1]; 681 | v2[2] = point3[2] - point1[2]; 682 | 683 | 684 | // Take the cross product of the two vectors to get 685 | // the normal vector. 686 | m3dCrossProduct3(result, v1, v2); 687 | } 688 | 689 | 690 | 691 | // Ditto above, but for doubles 692 | void m3dFindNormal(M3DVector3d result, const M3DVector3d point1, const M3DVector3d point2, 693 | const M3DVector3d point3) 694 | { 695 | M3DVector3d v1,v2; // Temporary vectors 696 | 697 | // Calculate two vectors from the three points. Assumes counter clockwise 698 | // winding! 699 | v1[0] = point1[0] - point2[0]; 700 | v1[1] = point1[1] - point2[1]; 701 | v1[2] = point1[2] - point2[2]; 702 | 703 | v2[0] = point2[0] - point3[0]; 704 | v2[1] = point2[1] - point3[1]; 705 | v2[2] = point2[2] - point3[2]; 706 | 707 | // Take the cross product of the two vectors to get 708 | // the normal vector. 709 | m3dCrossProduct3(result, v1, v2); 710 | } 711 | 712 | 713 | 714 | ///////////////////////////////////////////////////////////////////////////////////////// 715 | // Calculate the plane equation of the plane that the three specified points lay in. The 716 | // points are given in clockwise winding order, with normal pointing out of clockwise face 717 | // planeEq contains the A,B,C, and D of the plane equation coefficients 718 | void m3dGetPlaneEquation(M3DVector4f planeEq, const M3DVector3f p1, const M3DVector3f p2, const M3DVector3f p3) 719 | { 720 | // Get two vectors... do the cross product 721 | M3DVector3f v1, v2; 722 | 723 | // V1 = p3 - p1 724 | v1[0] = p3[0] - p1[0]; 725 | v1[1] = p3[1] - p1[1]; 726 | v1[2] = p3[2] - p1[2]; 727 | 728 | // V2 = P2 - p1 729 | v2[0] = p2[0] - p1[0]; 730 | v2[1] = p2[1] - p1[1]; 731 | v2[2] = p2[2] - p1[2]; 732 | 733 | // Unit normal to plane - Not sure which is the best way here 734 | m3dCrossProduct3(planeEq, v1, v2); 735 | m3dNormalizeVector3(planeEq); 736 | 737 | // Back substitute to get D 738 | planeEq[3] = -(planeEq[0] * p3[0] + planeEq[1] * p3[1] + planeEq[2] * p3[2]); 739 | } 740 | 741 | 742 | // Ditto above, but for doubles 743 | void m3dGetPlaneEquation(M3DVector4d planeEq, const M3DVector3d p1, const M3DVector3d p2, const M3DVector3d p3) 744 | { 745 | // Get two vectors... do the cross product 746 | M3DVector3d v1, v2; 747 | 748 | // V1 = p3 - p1 749 | v1[0] = p3[0] - p1[0]; 750 | v1[1] = p3[1] - p1[1]; 751 | v1[2] = p3[2] - p1[2]; 752 | 753 | // V2 = P2 - p1 754 | v2[0] = p2[0] - p1[0]; 755 | v2[1] = p2[1] - p1[1]; 756 | v2[2] = p2[2] - p1[2]; 757 | 758 | // Unit normal to plane - Not sure which is the best way here 759 | m3dCrossProduct3(planeEq, v1, v2); 760 | m3dNormalizeVector3(planeEq); 761 | // Back substitute to get D 762 | planeEq[3] = -(planeEq[0] * p3[0] + planeEq[1] * p3[1] + planeEq[2] * p3[2]); 763 | } 764 | 765 | 766 | ////////////////////////////////////////////////////////////////////////////////////////////////// 767 | // This function does a three dimensional Catmull-Rom curve interpolation. Pass four points, and a 768 | // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points. 769 | // Coded by RSW 770 | void m3dCatmullRom(M3DVector3f vOut, const M3DVector3f vP0, const M3DVector3f vP1, const M3DVector3f vP2, const M3DVector3f vP3, float t) 771 | { 772 | float t2 = t * t; 773 | float t3 = t2 * t; 774 | 775 | // X 776 | vOut[0] = 0.5f * ( ( 2.0f * vP1[0]) + 777 | (-vP0[0] + vP2[0]) * t + 778 | (2.0f * vP0[0] - 5.0f *vP1[0] + 4.0f * vP2[0] - vP3[0]) * t2 + 779 | (-vP0[0] + 3.0f*vP1[0] - 3.0f *vP2[0] + vP3[0]) * t3); 780 | // Y 781 | vOut[1] = 0.5f * ( ( 2.0f * vP1[1]) + 782 | (-vP0[1] + vP2[1]) * t + 783 | (2.0f * vP0[1] - 5.0f *vP1[1] + 4.0f * vP2[1] - vP3[1]) * t2 + 784 | (-vP0[1] + 3.0f*vP1[1] - 3.0f *vP2[1] + vP3[1]) * t3); 785 | 786 | // Z 787 | vOut[2] = 0.5f * ( ( 2.0f * vP1[2]) + 788 | (-vP0[2] + vP2[2]) * t + 789 | (2.0f * vP0[2] - 5.0f *vP1[2] + 4.0f * vP2[2] - vP3[2]) * t2 + 790 | (-vP0[2] + 3.0f*vP1[2] - 3.0f *vP2[2] + vP3[2]) * t3); 791 | } 792 | 793 | ////////////////////////////////////////////////////////////////////////////////////////////////// 794 | // This function does a two dimensional Catmull-Rom curve interpolation. Pass four points, and a 795 | // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points. 796 | // Coded by RSW 797 | void m3dCatmullRom2D(M3DVector2f vOut, const M3DVector2f vP0, const M3DVector2f vP1, const M3DVector2f vP2, const M3DVector2f vP3, float t) 798 | { 799 | float t2 = t * t; 800 | float t3 = t2 * t; 801 | 802 | // X 803 | vOut[0] = 0.5 * ( ( 2.0 * vP1[0]) + 804 | (-vP0[0] + vP2[0]) * t + 805 | (2.0 * vP0[0] - 5.0 *vP1[0] + 4.0 * vP2[0] - vP3[0]) * t2 + 806 | (-vP0[0] + 3.0*vP1[0] - 3.0 *vP2[0] + vP3[0]) * t3); 807 | // Y 808 | vOut[1] = 0.5 * ( ( 2.0 * vP1[1]) + 809 | (-vP0[1] + vP2[1]) * t + 810 | (2.0 * vP0[1] - 5.0 *vP1[1] + 4.0 * vP2[1] - vP3[1]) * t2 + 811 | (-vP0[1] + 3*vP1[1] - 3.0 *vP2[1] + vP3[1]) * t3); 812 | 813 | } 814 | 815 | 816 | 817 | ////////////////////////////////////////////////////////////////////////////////////////////////// 818 | // This function does a three dimensional Catmull-Rom curve interpolation. Pass four points, and a 819 | // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points. 820 | // Coded by RSW 821 | void m3dCatmullRom(M3DVector3d vOut, const M3DVector3d vP0, const M3DVector3d vP1, const M3DVector3d vP2, const M3DVector3d vP3, double t) 822 | { 823 | double t2 = t * t; 824 | double t3 = t2 * t; 825 | 826 | // X 827 | vOut[0] = 0.5 * ( ( 2.0 * vP1[0]) + 828 | (-vP0[0] + vP2[0]) * t + 829 | (2.0 * vP0[0] - 5.0 *vP1[0] + 4.0 * vP2[0] - vP3[0]) * t2 + 830 | (-vP0[0] + 3.0*vP1[0] - 3.0 *vP2[0] + vP3[0]) * t3); 831 | // Y 832 | vOut[1] = 0.5 * ( ( 2.0 * vP1[1]) + 833 | (-vP0[1] + vP2[1]) * t + 834 | (2.0 * vP0[1] - 5.0 *vP1[1] + 4.0 * vP2[1] - vP3[1]) * t2 + 835 | (-vP0[1] + 3*vP1[1] - 3.0 *vP2[1] + vP3[1]) * t3); 836 | 837 | // Z 838 | vOut[2] = 0.5 * ( ( 2.0 * vP1[2]) + 839 | (-vP0[2] + vP2[2]) * t + 840 | (2.0 * vP0[2] - 5.0 *vP1[2] + 4.0 * vP2[2] - vP3[2]) * t2 + 841 | (-vP0[2] + 3.0*vP1[2] - 3.0 *vP2[2] + vP3[2]) * t3); 842 | } 843 | 844 | 845 | ////////////////////////////////////////////////////////////////////////////////////////////////// 846 | // This function does a two dimensional Catmull-Rom curve interpolation. Pass four points, and a 847 | // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points. 848 | // Coded by RSW 849 | void m3dCatmullRom2D(M3DVector2d vOut, const M3DVector2d vP0, const M3DVector2d vP1, const M3DVector2d vP2, const M3DVector2d vP3, double t) 850 | { 851 | double t2 = t * t; 852 | double t3 = t2 * t; 853 | 854 | // X 855 | vOut[0] = 0.5 * ( ( 2.0 * vP1[0]) + 856 | (-vP0[0] + vP2[0]) * t + 857 | (2.0 * vP0[0] - 5.0 *vP1[0] + 4.0 * vP2[0] - vP3[0]) * t2 + 858 | (-vP0[0] + 3.0*vP1[0] - 3.0 *vP2[0] + vP3[0]) * t3); 859 | // Y 860 | vOut[1] = 0.5 * ( ( 2.0 * vP1[1]) + 861 | (-vP0[1] + vP2[1]) * t + 862 | (2.0 * vP0[1] - 5.0 *vP1[1] + 4.0 * vP2[1] - vP3[1]) * t2 + 863 | (-vP0[1] + 3*vP1[1] - 3.0 *vP2[1] + vP3[1]) * t3); 864 | 865 | } 866 | 867 | 868 | /////////////////////////////////////////////////////////////////////////////// 869 | // Determine if the ray (starting at point) intersects the sphere centered at 870 | // sphereCenter with radius sphereRadius 871 | // Return value is < 0 if the ray does not intersect 872 | // Return value is 0.0 if ray is tangent 873 | // Positive value is distance to the intersection point 874 | // Algorithm from "3D Math Primer for Graphics and Game Development" 875 | double m3dRaySphereTest(const M3DVector3d point, const M3DVector3d ray, const M3DVector3d sphereCenter, double sphereRadius) 876 | { 877 | //m3dNormalizeVector(ray); // Make sure ray is unit length 878 | 879 | M3DVector3d rayToCenter; // Ray to center of sphere 880 | rayToCenter[0] = sphereCenter[0] - point[0]; 881 | rayToCenter[1] = sphereCenter[1] - point[1]; 882 | rayToCenter[2] = sphereCenter[2] - point[2]; 883 | 884 | // Project rayToCenter on ray to test 885 | double a = m3dDotProduct3(rayToCenter, ray); 886 | 887 | // Distance to center of sphere 888 | double distance2 = m3dDotProduct3(rayToCenter, rayToCenter); // Or length 889 | 890 | 891 | double dRet = (sphereRadius * sphereRadius) - distance2 + (a*a); 892 | 893 | if(dRet > 0.0) // Return distance to intersection 894 | dRet = a - sqrt(dRet); 895 | 896 | return dRet; 897 | } 898 | 899 | /////////////////////////////////////////////////////////////////////////////// 900 | // Determine if the ray (starting at point) intersects the sphere centered at 901 | // ditto above, but uses floating point math 902 | float m3dRaySphereTest(const M3DVector3f point, const M3DVector3f ray, const M3DVector3f sphereCenter, float sphereRadius) 903 | { 904 | //m3dNormalizeVectorf(ray); // Make sure ray is unit length 905 | 906 | M3DVector3f rayToCenter; // Ray to center of sphere 907 | rayToCenter[0] = sphereCenter[0] - point[0]; 908 | rayToCenter[1] = sphereCenter[1] - point[1]; 909 | rayToCenter[2] = sphereCenter[2] - point[2]; 910 | 911 | // Project rayToCenter on ray to test 912 | float a = m3dDotProduct3(rayToCenter, ray); 913 | 914 | // Distance to center of sphere 915 | float distance2 = m3dDotProduct3(rayToCenter, rayToCenter); // Or length 916 | 917 | float dRet = (sphereRadius * sphereRadius) - distance2 + (a*a); 918 | 919 | if(dRet > 0.0) // Return distance to intersection 920 | dRet = a - sqrtf(dRet); 921 | 922 | return dRet; 923 | } 924 | 925 | 926 | /////////////////////////////////////////////////////////////////////////////////////////////////// 927 | // Calculate the tangent basis for a triangle on the surface of a model 928 | // This vector is needed for most normal mapping shaders 929 | void m3dCalculateTangentBasis(M3DVector3f vTangent, const M3DVector3f vTriangle[3], const M3DVector2f vTexCoords[3], const M3DVector3f N) 930 | { 931 | M3DVector3f dv2v1, dv3v1; 932 | float dc2c1t, dc2c1b, dc3c1t, dc3c1b; 933 | float M; 934 | 935 | m3dSubtractVectors3(dv2v1, vTriangle[1], vTriangle[0]); 936 | m3dSubtractVectors3(dv3v1, vTriangle[2], vTriangle[0]); 937 | 938 | dc2c1t = vTexCoords[1][0] - vTexCoords[0][0]; 939 | dc2c1b = vTexCoords[1][1] - vTexCoords[0][1]; 940 | dc3c1t = vTexCoords[2][0] - vTexCoords[0][0]; 941 | dc3c1b = vTexCoords[2][1] - vTexCoords[0][1]; 942 | 943 | M = (dc2c1t * dc3c1b) - (dc3c1t * dc2c1b); 944 | M = 1.0f / M; 945 | 946 | m3dScaleVector3(dv2v1, dc3c1b); 947 | m3dScaleVector3(dv3v1, dc2c1b); 948 | 949 | m3dSubtractVectors3(vTangent, dv2v1, dv3v1); 950 | m3dScaleVector3(vTangent, M); // This potentially changes the direction of the vector 951 | m3dNormalizeVector3(vTangent); 952 | 953 | M3DVector3f B; 954 | m3dCrossProduct3(B, N, vTangent); 955 | m3dCrossProduct3(vTangent, B, N); 956 | m3dNormalizeVector3(vTangent); 957 | } 958 | 959 | 960 | //////////////////////////////////////////////////////////////////////////// 961 | // Smoothly step between 0 and 1 between edge1 and edge 2 962 | double m3dSmoothStep(const double edge1, const double edge2, const double x) 963 | { 964 | double t; 965 | t = (x - edge1) / (edge2 - edge1); 966 | if(t > 1.0) 967 | t = 1.0; 968 | 969 | if(t < 0.0) 970 | t = 0.0f; 971 | 972 | return t * t * ( 3.0 - 2.0 * t); 973 | } 974 | 975 | //////////////////////////////////////////////////////////////////////////// 976 | // Smoothly step between 0 and 1 between edge1 and edge 2 977 | float m3dSmoothStep(const float edge1, const float edge2, const float x) 978 | { 979 | float t; 980 | t = (x - edge1) / (edge2 - edge1); 981 | if(t > 1.0f) 982 | t = 1.0f; 983 | 984 | if(t < 0.0) 985 | t = 0.0f; 986 | 987 | return t * t * ( 3.0f - 2.0f * t); 988 | } 989 | 990 | ///////////////////////////////////////////////////////////////////////////// 991 | // Smooth step for a vector 992 | void m3dSmoothStep(const M3DVector3f vEdge1, const M3DVector3f vEdge2, const float x, M3DVector3f vOut) 993 | { 994 | float t0; 995 | t0 = (x - vEdge1[0]) / (vEdge2[0] - vEdge1[0]); 996 | if(t0 > 1.0f) 997 | t0 = 1.0f; 998 | 999 | if(t0 < 0.0) 1000 | t0 = 0.0f; 1001 | 1002 | float t1; 1003 | t1 = (x - vEdge1[1]) / (vEdge2[1] - vEdge1[1]); 1004 | if(t1 > 1.0f) 1005 | t1 = 1.0f; 1006 | 1007 | if(t1 < 0.0) 1008 | t1 = 0.0f; 1009 | 1010 | 1011 | float t2; 1012 | t2 = (x - vEdge1[2]) / (vEdge2[2] - vEdge1[2]); 1013 | if(t2 > 1.0f) 1014 | t2 = 1.0f; 1015 | 1016 | if(t2 < 0.0) 1017 | t2 = 0.0f; 1018 | 1019 | 1020 | vOut[0] = t0 * t0 * ( 3.0f - 2.0f * t0); 1021 | vOut[1] = t1 * t1 * ( 3.0f - 2.0f * t1); 1022 | vOut[2] = t2 * t2 * ( 3.0f - 2.0f * t2); 1023 | } 1024 | 1025 | 1026 | ///////////////////////////////////////////////////////////////////////////// 1027 | // Smooth step for a vector 1028 | void m3dSmoothStep(const M3DVector3d vEdge1, const M3DVector3d vEdge2, const float x, M3DVector3d vOut) 1029 | { 1030 | double t0; 1031 | t0 = (x - vEdge1[0]) / (vEdge2[0] - vEdge1[0]); 1032 | if(t0 > 1.0) 1033 | t0 = 1.0; 1034 | 1035 | if(t0 < 0.0) 1036 | t0 = 0.0; 1037 | 1038 | double t1; 1039 | t1 = (x - vEdge1[1]) / (vEdge2[1] - vEdge1[1]); 1040 | if(t1 > 1.0) 1041 | t1 = 1.0; 1042 | 1043 | if(t1 < 0.0) 1044 | t1 = 0.0; 1045 | 1046 | double t2; 1047 | t2 = (x - vEdge1[2]) / (vEdge2[2] - vEdge1[2]); 1048 | if(t2 > 1.0) 1049 | t2 = 1.0; 1050 | 1051 | if(t2 < 0.0) 1052 | t2 = 0.0; 1053 | 1054 | 1055 | vOut[0] = t0 * t0 * ( 3.0 - 2.0 * t0); 1056 | vOut[1] = t1 * t1 * ( 3.0 - 2.0 * t1); 1057 | vOut[2] = t2 * t2 * ( 3.0 - 2.0 * t2); 1058 | } 1059 | 1060 | 1061 | /////////////////////////////////////////////////////////////////////////// 1062 | // Creae a projection to "squish" an object into the plane. 1063 | // Use m3dGetPlaneEquationf(planeEq, point1, point2, point3); 1064 | // to get a plane equation. 1065 | void m3dMakePlanarShadowMatrix(M3DMatrix44f proj, const M3DVector4f planeEq, const M3DVector3f vLightPos) 1066 | { 1067 | // These just make the code below easier to read. They will be 1068 | // removed by the optimizer. 1069 | float a = planeEq[0]; 1070 | float b = planeEq[1]; 1071 | float c = planeEq[2]; 1072 | float d = planeEq[3]; 1073 | 1074 | float dx = -vLightPos[0]; 1075 | float dy = -vLightPos[1]; 1076 | float dz = -vLightPos[2]; 1077 | 1078 | // Now build the projection matrix 1079 | proj[0] = b * dy + c * dz; 1080 | proj[1] = -a * dy; 1081 | proj[2] = -a * dz; 1082 | proj[3] = 0.0; 1083 | 1084 | proj[4] = -b * dx; 1085 | proj[5] = a * dx + c * dz; 1086 | proj[6] = -b * dz; 1087 | proj[7] = 0.0; 1088 | 1089 | proj[8] = -c * dx; 1090 | proj[9] = -c * dy; 1091 | proj[10] = a * dx + b * dy; 1092 | proj[11] = 0.0; 1093 | 1094 | proj[12] = -d * dx; 1095 | proj[13] = -d * dy; 1096 | proj[14] = -d * dz; 1097 | proj[15] = a * dx + b * dy + c * dz; 1098 | // Shadow matrix ready 1099 | } 1100 | 1101 | 1102 | /////////////////////////////////////////////////////////////////////////// 1103 | // Creae a projection to "squish" an object into the plane. 1104 | // Use m3dGetPlaneEquationd(planeEq, point1, point2, point3); 1105 | // to get a plane equation. 1106 | void m3dMakePlanarShadowMatrix(M3DMatrix44d proj, const M3DVector4d planeEq, const M3DVector3d vLightPos) 1107 | { 1108 | // These just make the code below easier to read. They will be 1109 | // removed by the optimizer. 1110 | double a = planeEq[0]; 1111 | double b = planeEq[1]; 1112 | double c = planeEq[2]; 1113 | double d = planeEq[3]; 1114 | 1115 | double dx = -vLightPos[0]; 1116 | double dy = -vLightPos[1]; 1117 | double dz = -vLightPos[2]; 1118 | 1119 | // Now build the projection matrix 1120 | proj[0] = b * dy + c * dz; 1121 | proj[1] = -a * dy; 1122 | proj[2] = -a * dz; 1123 | proj[3] = 0.0; 1124 | 1125 | proj[4] = -b * dx; 1126 | proj[5] = a * dx + c * dz; 1127 | proj[6] = -b * dz; 1128 | proj[7] = 0.0; 1129 | 1130 | proj[8] = -c * dx; 1131 | proj[9] = -c * dy; 1132 | proj[10] = a * dx + b * dy; 1133 | proj[11] = 0.0; 1134 | 1135 | proj[12] = -d * dx; 1136 | proj[13] = -d * dy; 1137 | proj[14] = -d * dz; 1138 | proj[15] = a * dx + b * dy + c * dz; 1139 | // Shadow matrix ready 1140 | } 1141 | 1142 | 1143 | ///////////////////////////////////////////////////////////////////////////// 1144 | // I want to know the point on a ray, closest to another given point in space. 1145 | // As a bonus, return the distance squared of the two points. 1146 | // In: vRayOrigin is the origin of the ray. 1147 | // In: vUnitRayDir is the unit vector of the ray 1148 | // In: vPointInSpace is the point in space 1149 | // Out: vPointOnRay is the poing on the ray closest to vPointInSpace 1150 | // Return: The square of the distance to the ray 1151 | double m3dClosestPointOnRay(M3DVector3d vPointOnRay, const M3DVector3d vRayOrigin, const M3DVector3d vUnitRayDir, 1152 | const M3DVector3d vPointInSpace) 1153 | { 1154 | M3DVector3d v; 1155 | m3dSubtractVectors3(v, vPointInSpace, vRayOrigin); 1156 | 1157 | double t = m3dDotProduct3(vUnitRayDir, v); 1158 | 1159 | // This is the point on the ray 1160 | vPointOnRay[0] = vRayOrigin[0] + (t * vUnitRayDir[0]); 1161 | vPointOnRay[1] = vRayOrigin[1] + (t * vUnitRayDir[1]); 1162 | vPointOnRay[2] = vRayOrigin[2] + (t * vUnitRayDir[2]); 1163 | 1164 | return m3dGetDistanceSquared3(vPointOnRay, vPointInSpace); 1165 | } 1166 | 1167 | // ditto above... but with floats 1168 | float m3dClosestPointOnRay(M3DVector3f vPointOnRay, const M3DVector3f vRayOrigin, const M3DVector3f vUnitRayDir, 1169 | const M3DVector3f vPointInSpace) 1170 | { 1171 | M3DVector3f v; 1172 | m3dSubtractVectors3(v, vPointInSpace, vRayOrigin); 1173 | 1174 | float t = m3dDotProduct3(vUnitRayDir, v); 1175 | 1176 | // This is the point on the ray 1177 | vPointOnRay[0] = vRayOrigin[0] + (t * vUnitRayDir[0]); 1178 | vPointOnRay[1] = vRayOrigin[1] + (t * vUnitRayDir[1]); 1179 | vPointOnRay[2] = vRayOrigin[2] + (t * vUnitRayDir[2]); 1180 | 1181 | return m3dGetDistanceSquared3(vPointOnRay, vPointInSpace); 1182 | } 1183 | -------------------------------------------------------------------------------- /math3d.h: -------------------------------------------------------------------------------- 1 | // Math3d.h 2 | // Math3D Library, version 0.95 3 | 4 | /* 5 | MIT License 6 | 7 | Copyright (c) 2007-2023 Richard S. Wright Jr. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | Contact GitHub API Training Shop Blog About 27 | */ 28 | 29 | // Header file for the Math3D library. The C-Runtime has math.h, this file and the 30 | // accompanying math3d.cpp are meant to suppliment math.h by adding geometry/math routines 31 | // useful for graphics, simulation, and physics applications (3D stuff). 32 | // This library is meant to be useful on Win32, Mac OS X, various Linux/Unix distros, 33 | // and mobile platforms. Although designed with OpenGL in mind, there are no OpenGL 34 | // dependencies. Other than standard math routines, the only other outside routine 35 | // used is memcpy (for faster copying of vector arrays). 36 | // Richard S. Wright Jr. 37 | 38 | #ifndef _MATH3D_LIBRARY__ 39 | #define _MATH3D_LIBRARY__ 40 | 41 | #include 42 | #include // Memcpy lives here on most systems 43 | 44 | /////////////////////////////////////////////////////////////////////////////// 45 | // Data structures and containers 46 | // Much thought went into how these are declared. Many libraries declare these 47 | // as structures with x, y, z data members. However structure alignment issues 48 | // could limit the portability of code based on such structures, or the binary 49 | // compatibility of data files (more likely) that contain such structures across 50 | // compilers/platforms. Arrays are always tightly packed, and are more efficient 51 | // for moving blocks of data around (usually). 52 | // Sigh... yes, I probably should use GLfloat, etc. But that requires that we 53 | // always include OpenGL. Since this library is also useful for non-graphical 54 | // applications, I shall risk the wrath of the portability gods... 55 | 56 | typedef float M3DVector2f[2]; // 3D points = 3D Vectors, but we need a 57 | typedef double M3DVector2d[2]; // 2D representations sometimes... (x,y) order 58 | 59 | typedef float M3DVector3f[3]; // Vector of three floats (x, y, z) 60 | typedef double M3DVector3d[3]; // Vector of three doubles (x, y, z) 61 | 62 | typedef float M3DVector4f[4]; // Lesser used... Do we really need these? 63 | typedef double M3DVector4d[4]; // Yes, occasionaly we do need a trailing w component 64 | 65 | 66 | 67 | // 3x3 matrix - column major. X vector is 0, 1, 2, etc. 68 | // 0 3 6 69 | // 1 4 7 70 | // 2 5 8 71 | typedef float M3DMatrix33f[9]; // A 3 x 3 matrix, column major (floats) - OpenGL Style 72 | typedef double M3DMatrix33d[9]; // A 3 x 3 matrix, column major (doubles) - OpenGL Style 73 | 74 | 75 | // 4x4 matrix - column major. X vector is 0, 1, 2, etc. 76 | // 0 4 8 12 77 | // 1 5 9 13 78 | // 2 6 10 14 79 | // 3 7 11 15 80 | typedef float M3DMatrix44f[16]; // A 4 X 4 matrix, column major (floats) - OpenGL style 81 | typedef double M3DMatrix44d[16]; // A 4 x 4 matrix, column major (doubles) - OpenGL style 82 | 83 | 84 | /////////////////////////////////////////////////////////////////////////////// 85 | // Useful constants 86 | constexpr double M3D_PI = 3.14159265358979323846; 87 | constexpr double M3D_2PI = 2.0 * M3D_PI; 88 | constexpr double M3D_PI_DIV_2 = 0.5 * M3D_PI; 89 | constexpr double M3D_PI_DIV_180 = 0.017453292519943296; 90 | constexpr double M3D_INV_PI_DIV_180 = 57.2957795130823229; 91 | 92 | 93 | /////////////////////////////////////////////////////////////////////////////// 94 | // Useful shortcuts and macros 95 | // Radians are king... but we need a way to swap back and forth for programmers and presentation. 96 | // Leaving these as Macros instead of inline functions, causes constants 97 | // to be evaluated at compile time instead of run time, e.g. m3dDegToRad(90.0) 98 | constexpr auto m3dDegToRad(double x) -> double { return x*M3D_PI_DIV_180; } 99 | constexpr auto m3dRadToDeg(double x) -> double { return x*M3D_INV_PI_DIV_180; } 100 | 101 | // Hour angles 102 | constexpr auto m3dHrToDeg(double x) -> double { return x * (1.0 * 15.0); } 103 | 104 | constexpr auto m3dHrToRad(double x) -> double { return m3dDegToRad(m3dHrToDeg(x)); } 105 | 106 | constexpr auto m3dDegToHr(double x) ->double { return ((x) / 15.0); } 107 | constexpr auto m3dRadToHr(double x) ->double { return m3dDegToHr(m3dRadToDeg(x)); } 108 | 109 | 110 | // Returns the same number if it is a power of 111 | // two. Returns a larger integer if it is not a 112 | // power of two. The larger integer is the next 113 | // highest power of two. 114 | inline unsigned int m3dIsPOW2(unsigned int iValue) 115 | { 116 | unsigned int nPow2 = 1; 117 | 118 | while(iValue > nPow2) 119 | nPow2 = (nPow2 << 1); 120 | 121 | return nPow2; 122 | } 123 | 124 | 125 | /////////////////////////////////////////////////////////////////////////////// 126 | // Inline accessor functions (Macros) for people who just can't count to 3 or 4 127 | // Really... you should learn to count before you learn to program ;-) 128 | // 0 = x 129 | // 1 = y 130 | // 2 = z 131 | // 3 = w 132 | #define m3dGetVectorX(v) (v[0]) 133 | #define m3dGetVectorY(v) (v[1]) 134 | #define m3dGetVectorZ(v) (v[2]) 135 | #define m3dGetVectorW(v) (v[3]) 136 | 137 | #define m3dSetVectorX(v, x) ((v)[0] = (x)) 138 | #define m3dSetVectorY(v, y) ((v)[1] = (y)) 139 | #define m3dSetVectorZ(v, z) ((v)[2] = (z)) 140 | #define m3dSetVectorW(v, w) ((v)[3] = (w)) 141 | 142 | /////////////////////////////////////////////////////////////////////////////// 143 | // Inline vector functions 144 | // Load Vector with (x, y, z, w). 145 | inline void m3dLoadVector2(M3DVector2f v, const float x, const float y) 146 | { v[0] = x; v[1] = y; } 147 | inline void m3dLoadVector2(M3DVector2d v, const float x, const float y) 148 | { v[0] = x; v[1] = y; } 149 | inline void m3dLoadVector3(M3DVector3f v, const float x, const float y, const float z) 150 | { v[0] = x; v[1] = y; v[2] = z; } 151 | inline void m3dLoadVector3(M3DVector3d v, const double x, const double y, const double z) 152 | { v[0] = x; v[1] = y; v[2] = z; } 153 | inline void m3dLoadVector4(M3DVector4f v, const float x, const float y, const float z, const float w) 154 | { v[0] = x; v[1] = y; v[2] = z; v[3] = w;} 155 | inline void m3dLoadVector4(M3DVector4d v, const double x, const double y, const double z, const double w) 156 | { v[0] = x; v[1] = y; v[2] = z; v[3] = w;} 157 | 158 | 159 | //////////////////////////////////////////////////////////////////////////////// 160 | // Copy vector src into vector dst 161 | inline void m3dCopyVector2(M3DVector2f dst, const M3DVector2f src) { memcpy(dst, src, sizeof(M3DVector2f)); } 162 | inline void m3dCopyVector2(M3DVector2d dst, const M3DVector2d src) { memcpy(dst, src, sizeof(M3DVector2d)); } 163 | 164 | inline void m3dCopyVector3(M3DVector3f dst, const M3DVector3f src) { memcpy(dst, src, sizeof(M3DVector3f)); } 165 | inline void m3dCopyVector3(M3DVector3d dst, const M3DVector3d src) { memcpy(dst, src, sizeof(M3DVector3d)); } 166 | 167 | inline void m3dCopyVector4(M3DVector4f dst, const M3DVector4f src) { memcpy(dst, src, sizeof(M3DVector4f)); } 168 | inline void m3dCopyVector4(M3DVector4d dst, const M3DVector4d src) { memcpy(dst, src, sizeof(M3DVector4d)); } 169 | 170 | 171 | //////////////////////////////////////////////////////////////////////////////// 172 | // Add Vectors (r, a, b) r = a + b 173 | inline void m3dAddVectors2(M3DVector2f r, const M3DVector2f a, const M3DVector2f b) 174 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; } 175 | inline void m3dAddVectors2(M3DVector2d r, const M3DVector2d a, const M3DVector2d b) 176 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; } 177 | 178 | inline void m3dAddVectors3(M3DVector3f r, const M3DVector3f a, const M3DVector3f b) 179 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; } 180 | inline void m3dAddVectors3(M3DVector3d r, const M3DVector3d a, const M3DVector3d b) 181 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; } 182 | 183 | inline void m3dAddVectors4(M3DVector4f r, const M3DVector4f a, const M3DVector4f b) 184 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; r[3] = a[3] + b[3]; } 185 | inline void m3dAddVectors4(M3DVector4d r, const M3DVector4d a, const M3DVector4d b) 186 | { r[0] = a[0] + b[0]; r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; r[3] = a[3] + b[3]; } 187 | 188 | //////////////////////////////////////////////////////////////////////////////// 189 | // Subtract Vectors (r, a, b) r = a - b 190 | inline void m3dSubtractVectors2(M3DVector2f r, const M3DVector2f a, const M3DVector2f b) 191 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; } 192 | inline void m3dSubtractVectors2(M3DVector2d r, const M3DVector2d a, const M3DVector2d b) 193 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; } 194 | 195 | inline void m3dSubtractVectors3(M3DVector3f r, const M3DVector3f a, const M3DVector3f b) 196 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; } 197 | inline void m3dSubtractVectors3(M3DVector3d r, const M3DVector3d a, const M3DVector3d b) 198 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; } 199 | 200 | inline void m3dSubtractVectors4(M3DVector4f r, const M3DVector4f a, const M3DVector4f b) 201 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; r[3] = a[3] - b[3]; } 202 | inline void m3dSubtractVectors4(M3DVector4d r, const M3DVector4d a, const M3DVector4d b) 203 | { r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; r[3] = a[3] - b[3]; } 204 | 205 | 206 | 207 | /////////////////////////////////////////////////////////////////////////////////////// 208 | // Scale Vectors (in place) 209 | inline void m3dScaleVector2(M3DVector2f v, const float scale) 210 | { v[0] *= scale; v[1] *= scale; } 211 | inline void m3dScaleVector2(M3DVector2d v, const double scale) 212 | { v[0] *= scale; v[1] *= scale; } 213 | 214 | inline void m3dScaleVector3(M3DVector3f v, const float scale) 215 | { v[0] *= scale; v[1] *= scale; v[2] *= scale; } 216 | inline void m3dScaleVector3(M3DVector3d v, const double scale) 217 | { v[0] *= scale; v[1] *= scale; v[2] *= scale; } 218 | 219 | inline void m3dScaleVector4(M3DVector4f v, const float scale) 220 | { v[0] *= scale; v[1] *= scale; v[2] *= scale; v[3] *= scale; } 221 | inline void m3dScaleVector4(M3DVector4d v, const double scale) 222 | { v[0] *= scale; v[1] *= scale; v[2] *= scale; v[3] *= scale; } 223 | 224 | 225 | ////////////////////////////////////////////////////////////////////////////////////// 226 | // Cross Product 227 | // u x v = result 228 | // 3 component vectors only. 229 | inline void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v) 230 | { 231 | result[0] = u[1]*v[2] - v[1]*u[2]; 232 | result[1] = -u[0]*v[2] + v[0]*u[2]; 233 | result[2] = u[0]*v[1] - v[0]*u[1]; 234 | } 235 | 236 | inline void m3dCrossProduct3(M3DVector3d result, const M3DVector3d u, const M3DVector3d v) 237 | { 238 | result[0] = u[1]*v[2] - v[1]*u[2]; 239 | result[1] = -u[0]*v[2] + v[0]*u[2]; 240 | result[2] = u[0]*v[1] - v[0]*u[1]; 241 | } 242 | 243 | ////////////////////////////////////////////////////////////////////////////////////// 244 | // Dot Product, only for three component vectors 245 | // return u dot v 246 | inline float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v) 247 | { return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; } 248 | 249 | inline double m3dDotProduct3(const M3DVector3d u, const M3DVector3d v) 250 | { return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; } 251 | 252 | ////////////////////////////////////////////////////////////////////////////////////// 253 | // Angle between vectors, only for three component vectors. Angle is in radians... 254 | inline float m3dGetAngleBetweenVectors3(const M3DVector3f u, const M3DVector3f v) 255 | { 256 | float dTemp = m3dDotProduct3(u, v); 257 | return float(acos(double(dTemp))); // Double cast just gets rid of compiler warning, no real need 258 | } 259 | 260 | inline double m3dGetAngleBetweenVectors3(const M3DVector3d u, const M3DVector3d v) 261 | { 262 | double dTemp = m3dDotProduct3(u, v); 263 | return acos(dTemp); 264 | } 265 | 266 | ////////////////////////////////////////////////////////////////////////////////////// 267 | // Get Square of a vectors length 268 | // Only for three component vectors 269 | inline float m3dGetVectorLengthSquared3(const M3DVector3f u) 270 | { return (u[0] * u[0]) + (u[1] * u[1]) + (u[2] * u[2]); } 271 | 272 | inline double m3dGetVectorLengthSquared3(const M3DVector3d u) 273 | { return (u[0] * u[0]) + (u[1] * u[1]) + (u[2] * u[2]); } 274 | 275 | ////////////////////////////////////////////////////////////////////////////////////// 276 | // Get lenght of vector 277 | // Only for three component vectors. 278 | inline float m3dGetVectorLength3(const M3DVector3f u) 279 | { return sqrtf(m3dGetVectorLengthSquared3(u)); } 280 | 281 | inline double m3dGetVectorLength3(const M3DVector3d u) 282 | { return sqrt(m3dGetVectorLengthSquared3(u)); } 283 | 284 | ////////////////////////////////////////////////////////////////////////////////////// 285 | // Normalize a vector 286 | // Scale a vector to unit length. Easy, just scale the vector by it's length 287 | inline void m3dNormalizeVector3(M3DVector3f u) 288 | { m3dScaleVector3(u, 1.0f / m3dGetVectorLength3(u)); } 289 | 290 | inline void m3dNormalizeVector3(M3DVector3d u) 291 | { m3dScaleVector3(u, 1.0 / m3dGetVectorLength3(u)); } 292 | 293 | 294 | ////////////////////////////////////////////////////////////////////////////////////// 295 | // Get the distance between two points. The distance between two points is just 296 | // the magnitude of the difference between two vectors 297 | // Located in math.cpp 298 | float m3dGetDistanceSquared3(const M3DVector3f u, const M3DVector3f v); 299 | double m3dGetDistanceSquared3(const M3DVector3d u, const M3DVector3d v); 300 | 301 | inline double m3dGetDistance3(const M3DVector3d u, const M3DVector3d v) 302 | { return sqrt(m3dGetDistanceSquared3(u, v)); } 303 | 304 | inline float m3dGetDistance3(const M3DVector3f u, const M3DVector3f v) 305 | { return sqrtf(m3dGetDistanceSquared3(u, v)); } 306 | 307 | inline float m3dGetMagnitudeSquared3(const M3DVector3f u) { return u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; } 308 | inline double m3dGetMagnitudeSquared3(const M3DVector3d u) { return u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; } 309 | 310 | inline float m3dGetMagnitude3(const M3DVector3f u) { return sqrtf(m3dGetMagnitudeSquared3(u)); } 311 | inline double m3dGetMagnitude3(const M3DVector3d u) { return sqrt(m3dGetMagnitudeSquared3(u)); } 312 | 313 | 314 | 315 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 316 | // Matrix functions 317 | // Both floating point and double precision 3x3 and 4x4 matricies are supported. 318 | // No support is included for arbitrarily dimensioned matricies on purpose, since 319 | // the 3x3 and 4x4 matrix routines are the most common for the purposes of this 320 | // library. Matrices are column major, like OpenGL matrices. 321 | // Unlike the vector functions, some of these are going to have to not be inlined, 322 | // although many will be. 323 | 324 | // Copy Matrix 325 | // Brain-dead memcpy 326 | inline void m3dCopyMatrix33(M3DMatrix33f dst, const M3DMatrix33f src) 327 | { memcpy(dst, src, sizeof(M3DMatrix33f)); } 328 | 329 | inline void m3dCopyMatrix33(M3DMatrix33d dst, const M3DMatrix33d src) 330 | { memcpy(dst, src, sizeof(M3DMatrix33d)); } 331 | 332 | inline void m3dCopyMatrix44(M3DMatrix44f dst, const M3DMatrix44f src) 333 | { memcpy(dst, src, sizeof(M3DMatrix44f)); } 334 | 335 | inline void m3dCopyMatrix44(M3DMatrix44d dst, const M3DMatrix44d src) 336 | { memcpy(dst, src, sizeof(M3DMatrix44d)); } 337 | 338 | // LoadIdentity 339 | // Implemented in Math3d.cpp 340 | void m3dLoadIdentity33(M3DMatrix33f m); 341 | void m3dLoadIdentity33(M3DMatrix33d m); 342 | void m3dLoadIdentity44(M3DMatrix44f m); 343 | void m3dLoadIdentity44(M3DMatrix44d m); 344 | 345 | ///////////////////////////////////////////////////////////////////////////// 346 | // Get/Set Column. 347 | inline void m3dGetMatrixColumn33(M3DVector3f dst, const M3DMatrix33f src, const int column) 348 | { memcpy(dst, src + (3 * column), sizeof(float) * 3); } 349 | 350 | inline void m3dGetMatrixColumn33(M3DVector3d dst, const M3DMatrix33d src, const int column) 351 | { memcpy(dst, src + (3 * column), sizeof(double) * 3); } 352 | 353 | inline void m3dSetMatrixColumn33(M3DMatrix33f dst, const M3DVector3f src, const int column) 354 | { memcpy(dst + (3 * column), src, sizeof(float) * 3); } 355 | 356 | inline void m3dSetMatrixColumn33(M3DMatrix33d dst, const M3DVector3d src, const int column) 357 | { memcpy(dst + (3 * column), src, sizeof(double) * 3); } 358 | 359 | inline void m3dGetMatrixColumn44(M3DVector4f dst, const M3DMatrix44f src, const int column) 360 | { memcpy(dst, src + (4 * column), sizeof(float) * 4); } 361 | 362 | inline void m3dGetMatrixColumn44(M3DVector4d dst, const M3DMatrix44d src, const int column) 363 | { memcpy(dst, src + (4 * column), sizeof(double) * 4); } 364 | 365 | inline void m3dSetMatrixColumn44(M3DMatrix44f dst, const M3DVector4f src, const int column) 366 | { memcpy(dst + (4 * column), src, sizeof(float) * 4); } 367 | 368 | inline void m3dSetMatrixColumn44(M3DMatrix44d dst, const M3DVector4d src, const int column) 369 | { memcpy(dst + (4 * column), src, sizeof(double) * 4); } 370 | 371 | 372 | /////////////////////////////////////////////////////////////////////////////// 373 | // Extract a rotation matrix from a 4x4 matrix 374 | // Extracts the rotation matrix (3x3) from a 4x4 matrix 375 | inline void m3dExtractRotationMatrix33(M3DMatrix33f dst, const M3DMatrix44f src) 376 | { 377 | memcpy(dst, src, sizeof(float) * 3); // X column 378 | memcpy(dst + 3, src + 4, sizeof(float) * 3); // Y column 379 | memcpy(dst + 6, src + 8, sizeof(float) * 3); // Z column 380 | } 381 | 382 | // Ditto above, but for doubles 383 | inline void m3dExtractRotationMatrix33(M3DMatrix33d dst, const M3DMatrix44d src) 384 | { 385 | memcpy(dst, src, sizeof(double) * 3); // X column 386 | memcpy(dst + 3, src + 4, sizeof(double) * 3); // Y column 387 | memcpy(dst + 6, src + 8, sizeof(double) * 3); // Z column 388 | } 389 | 390 | // Inject Rotation (3x3) into a full 4x4 matrix... 391 | inline void m3dInjectRotationMatrix44(M3DMatrix44f dst, const M3DMatrix33f src) 392 | { 393 | memcpy(dst, src, sizeof(float) * 4); 394 | memcpy(dst + 4, src + 4, sizeof(float) * 4); 395 | memcpy(dst + 8, src + 8, sizeof(float) * 4); 396 | } 397 | 398 | // Ditto above for doubles 399 | inline void m3dInjectRotationMatrix44(M3DMatrix44d dst, const M3DMatrix33d src) 400 | { 401 | memcpy(dst, src, sizeof(double) * 4); 402 | memcpy(dst + 4, src + 4, sizeof(double) * 4); 403 | memcpy(dst + 8, src + 8, sizeof(double) * 4); 404 | } 405 | 406 | //////////////////////////////////////////////////////////////////////////////// 407 | // MultMatrix 408 | // Implemented in Math.cpp 409 | void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b); 410 | void m3dMatrixMultiply44(M3DMatrix44d product, const M3DMatrix44d a, const M3DMatrix44d b); 411 | void m3dMatrixMultiply33(M3DMatrix33f product, const M3DMatrix33f a, const M3DMatrix33f b); 412 | void m3dMatrixMultiply33(M3DMatrix33d product, const M3DMatrix33d a, const M3DMatrix33d b); 413 | 414 | 415 | // Transform - Does rotation and translation via a 4x4 matrix. Transforms 416 | // a point or vector. 417 | // By-the-way __inline means I'm asking the compiler to do a cost/benefit analysis. If 418 | // these are used frequently, they may not be inlined to save memory. I'm experimenting 419 | // with this.... 420 | // Just transform a 3 compoment vector 421 | __inline void m3dTransformVector3(M3DVector3f vOut, const M3DVector3f v, const M3DMatrix44f m) 422 | { 423 | vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12];// * v[3]; // Assuming 1 424 | vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13];// * v[3]; 425 | vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14];// * v[3]; 426 | //vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; 427 | } 428 | 429 | // Ditto above, but for doubles 430 | __inline void m3dTransformVector3(M3DVector3d vOut, const M3DVector3d v, const M3DMatrix44d m) 431 | { 432 | vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12];// * v[3]; 433 | vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13];// * v[3]; 434 | vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14];// * v[3]; 435 | //vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; 436 | } 437 | 438 | // Full four component transform 439 | __inline void m3dTransformVector4(M3DVector4f vOut, const M3DVector4f v, const M3DMatrix44f m) 440 | { 441 | vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3]; 442 | vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3]; 443 | vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3]; 444 | vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; 445 | } 446 | 447 | // Ditto above, but for doubles 448 | __inline void m3dTransformVector4(M3DVector4d vOut, const M3DVector4d v, const M3DMatrix44d m) 449 | { 450 | vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3]; 451 | vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3]; 452 | vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3]; 453 | vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; 454 | } 455 | 456 | 457 | 458 | // Just do the rotation, not the translation... this is usually done with a 3x3 459 | // Matrix. 460 | __inline void m3dRotateVector(M3DVector3f vOut, const M3DVector3f p, const M3DMatrix33f m) 461 | { 462 | vOut[0] = m[0] * p[0] + m[3] * p[1] + m[6] * p[2]; 463 | vOut[1] = m[1] * p[0] + m[4] * p[1] + m[7] * p[2]; 464 | vOut[2] = m[2] * p[0] + m[5] * p[1] + m[8] * p[2]; 465 | } 466 | 467 | // Ditto above, but for doubles 468 | __inline void m3dRotateVector(M3DVector3d vOut, const M3DVector3d p, const M3DMatrix33d m) 469 | { 470 | vOut[0] = m[0] * p[0] + m[3] * p[1] + m[6] * p[2]; 471 | vOut[1] = m[1] * p[0] + m[4] * p[1] + m[7] * p[2]; 472 | vOut[2] = m[2] * p[0] + m[5] * p[1] + m[8] * p[2]; 473 | } 474 | 475 | 476 | // Create a Scaling Matrix 477 | inline void m3dScaleMatrix33(M3DMatrix33f m, float xScale, float yScale, float zScale) 478 | { m3dLoadIdentity33(m); m[0] = xScale; m[4] = yScale; m[8] = zScale; } 479 | 480 | inline void m3dScaleMatrix33(M3DMatrix33f m, const M3DVector3f vScale) 481 | { m3dLoadIdentity33(m); m[0] = vScale[0]; m[4] = vScale[1]; m[8] = vScale[2]; } 482 | 483 | inline void m3dScaleMatrix33(M3DMatrix33d m, double xScale, double yScale, double zScale) 484 | { m3dLoadIdentity33(m); m[0] = xScale; m[4] = yScale; m[8] = zScale; } 485 | 486 | inline void m3dScaleMatrix33(M3DMatrix33d m, const M3DVector3d vScale) 487 | { m3dLoadIdentity33(m); m[0] = vScale[0]; m[4] = vScale[1]; m[8] = vScale[2]; } 488 | 489 | inline void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale) 490 | { m3dLoadIdentity44(m); m[0] = xScale; m[5] = yScale; m[10] = zScale; } 491 | 492 | inline void m3dScaleMatrix44(M3DMatrix44f m, const M3DVector3f vScale) 493 | { m3dLoadIdentity44(m); m[0] = vScale[0]; m[5] = vScale[1]; m[10] = vScale[2]; } 494 | 495 | inline void m3dScaleMatrix44(M3DMatrix44d m, double xScale, double yScale, double zScale) 496 | { m3dLoadIdentity44(m); m[0] = xScale; m[5] = yScale; m[10] = zScale; } 497 | 498 | inline void m3dScaleMatrix44(M3DMatrix44d m, const M3DVector3d vScale) 499 | { m3dLoadIdentity44(m); m[0] = vScale[0]; m[5] = vScale[1]; m[10] = vScale[2]; } 500 | 501 | 502 | void m3dMakePerspectiveMatrix(M3DMatrix44f mProjection, float fFov, float fAspect, float zMin, float zMax); 503 | void m3dMakeOrthographicMatrix(M3DMatrix44f mProjection, float xMin, float xMax, float yMin, float yMax, float zMin, float zMax); 504 | 505 | 506 | // Create a Rotation matrix 507 | // Implemented in math3d.cpp 508 | void m3dRotationMatrix33(M3DMatrix33f m, float angle, float x, float y, float z); 509 | void m3dRotationMatrix33(M3DMatrix33d m, double angle, double x, double y, double z); 510 | void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z); 511 | void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z); 512 | 513 | // Create a Translation matrix. Only 4x4 matrices have translation components 514 | inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z) 515 | { m3dLoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z; } 516 | 517 | inline void m3dTranslationMatrix44(M3DMatrix44d m, double x, double y, double z) 518 | { m3dLoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z; } 519 | 520 | void m3dInvertMatrix44(M3DMatrix44f mInverse, const M3DMatrix44f m); 521 | void m3dInvertMatrix44(M3DMatrix44d mInverse, const M3DMatrix44d m); 522 | 523 | /////////////////////////////////////////////////////////////////////////////// 524 | /////////////////////////////////////////////////////////////////////////////// 525 | /////////////////////////////////////////////////////////////////////////////// 526 | // Other Miscellaneous functions 527 | 528 | // Find a normal from three points 529 | // Implemented in math3d.cpp 530 | void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, 531 | const M3DVector3f point3); 532 | void m3dFindNormal(M3DVector3d result, const M3DVector3d point1, const M3DVector3d point2, 533 | const M3DVector3d point3); 534 | 535 | 536 | // Calculates the signed distance of a point to a plane 537 | inline float m3dGetDistanceToPlane(const M3DVector3f point, const M3DVector4f plane) 538 | { return point[0]*plane[0] + point[1]*plane[1] + point[2]*plane[2] + plane[3]; } 539 | 540 | inline double m3dGetDistanceToPlane(const M3DVector3d point, const M3DVector4d plane) 541 | { return point[0]*plane[0] + point[1]*plane[1] + point[2]*plane[2] + plane[3]; } 542 | 543 | 544 | // Get plane equation from three points 545 | void m3dGetPlaneEquation(M3DVector4f planeEq, const M3DVector3f p1, const M3DVector3f p2, const M3DVector3f p3); 546 | void m3dGetPlaneEquation(M3DVector4d planeEq, const M3DVector3d p1, const M3DVector3d p2, const M3DVector3d p3); 547 | 548 | // Determine if a ray intersects a sphere 549 | // Return value is < 0 if the ray does not intersect 550 | // Return value is 0.0 if ray is tangent 551 | // Positive value is distance to the intersection point 552 | double m3dRaySphereTest(const M3DVector3d point, const M3DVector3d ray, const M3DVector3d sphereCenter, double sphereRadius); 553 | float m3dRaySphereTest(const M3DVector3f point, const M3DVector3f ray, const M3DVector3f sphereCenter, float sphereRadius); 554 | 555 | 556 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 557 | // Faster (and one shortcut) replacements for gluProject 558 | void m3dProjectXY( M3DVector2f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn); 559 | void m3dProjectXYZ(M3DVector3f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn); 560 | 561 | 562 | ////////////////////////////////////////////////////////////////////////////////////////////////// 563 | // This function does a three dimensional Catmull-Rom "spline" interpolation between p1 and p2 564 | void m3dCatmullRom(M3DVector3f vOut, const M3DVector3f vP0, const M3DVector3f vP1, const M3DVector3f vP2, const M3DVector3f vP3, float t); 565 | void m3dCatmullRom(M3DVector3d vOut, const M3DVector3d vP0, const M3DVector3d vP1, const M3DVector3d vP2, const M3DVector3d vP3, double t); 566 | void m3dCatmullRom2D(M3DVector2d vOut, const M3DVector2d vP0, const M3DVector2d vP1, const M3DVector2d vP2, const M3DVector2d vP3, double t); 567 | void m3dCatmullRom2D(M3DVector2f vOut, const M3DVector2f vP0, const M3DVector2f vP1, const M3DVector2f vP2, const M3DVector2f vP3, float t); 568 | 569 | 570 | ////////////////////////////////////////////////////////////////////////////////////////////////// 571 | // Compare floats and doubles... 572 | inline bool m3dCloseEnough(const float fCandidate, const float fCompare, const float fEpsilon) 573 | { 574 | return (fabs(fCandidate - fCompare) < fEpsilon); 575 | } 576 | 577 | inline bool m3dCloseEnough(const double dCandidate, const double dCompare, const double dEpsilon) 578 | { 579 | return (fabs(dCandidate - dCompare) < dEpsilon); 580 | } 581 | 582 | //////////////////////////////////////////////////////////////////////////// 583 | // Used for normal mapping. Finds the tangent bases for a triangle... 584 | // Only a floating point implementation is provided. This has no practical use as doubles. 585 | void m3dCalculateTangentBasis(M3DVector3f vTangent, const M3DVector3f pvTriangle[3], const M3DVector2f pvTexCoords[3], const M3DVector3f N); 586 | 587 | //////////////////////////////////////////////////////////////////////////// 588 | // Smoothly step between 0 and 1 between edge1 and edge 2 589 | double m3dSmoothStep(const double edge1, const double edge2, const double x); 590 | float m3dSmoothStep(const float edge1, const float edge2, const float x); 591 | void m3dSmoothStep(const M3DVector3f vEdge1, const M3DVector3f vEdge2, const float x, M3DVector3f vOut); 592 | void m3dSmoothStep(const M3DVector3d vEdge1, const M3DVector3d vEdge2, const double x, M3DVector3d vOut); 593 | 594 | 595 | 596 | ///////////////////////////////////////////////////////////////////////////// 597 | // Planar shadow Matrix 598 | void m3dMakePlanarShadowMatrix(M3DMatrix44d proj, const M3DVector4d planeEq, const M3DVector3d vLightPos); 599 | void m3dMakePlanarShadowMatrix(M3DMatrix44f proj, const M3DVector4f planeEq, const M3DVector3f vLightPos); 600 | 601 | ///////////////////////////////////////////////////////////////////////////// 602 | // Closest point on a ray to another point in space 603 | double m3dClosestPointOnRay(M3DVector3d vPointOnRay, const M3DVector3d vRayOrigin, const M3DVector3d vUnitRayDir, 604 | const M3DVector3d vPointInSpace); 605 | 606 | float m3dClosestPointOnRay(M3DVector3f vPointOnRay, const M3DVector3f vRayOrigin, const M3DVector3f vUnitRayDir, 607 | const M3DVector3f vPointInSpace); 608 | 609 | ////////////////////////////////////////////////////////////////////////////// 610 | // Miscellany... 611 | void m3dPerform2DRotationOnPoint(const double inXY[2], double outXY[2], double rotDegrees); 612 | 613 | 614 | 615 | #endif 616 | --------------------------------------------------------------------------------