| | | 1 | | //======================================================================= |
| | | 2 | | // Fixed4x4.Factories.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024–present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System; |
| | | 9 | | using System.Runtime.CompilerServices; |
| | | 10 | | |
| | | 11 | | namespace FixedMathSharp; |
| | | 12 | | |
| | | 13 | | /// <content> |
| | | 14 | | /// Factory methods for constructing <see cref="Fixed4x4"/> matrices, including |
| | | 15 | | /// translation, rotation, scale, and other common transformation matrices. |
| | | 16 | | /// </content> |
| | | 17 | | public partial struct Fixed4x4 |
| | | 18 | | { |
| | | 19 | | #region Static Matrix Generators and Transformations |
| | | 20 | | |
| | | 21 | | /// <summary> |
| | | 22 | | /// Creates a translation matrix from the specified 3-dimensional vector. |
| | | 23 | | /// </summary> |
| | | 24 | | /// <param name="position"></param> |
| | | 25 | | /// <returns>The translation matrix.</returns> |
| | | 26 | | public static Fixed4x4 CreateTranslation(Vector3d position) |
| | | 27 | | { |
| | 13 | 28 | | Fixed4x4 result = default; |
| | 13 | 29 | | result.M11 = Fixed64.One; |
| | 13 | 30 | | result.M12 = Fixed64.Zero; |
| | 13 | 31 | | result.M13 = Fixed64.Zero; |
| | 13 | 32 | | result.M14 = Fixed64.Zero; |
| | 13 | 33 | | result.M21 = Fixed64.Zero; |
| | 13 | 34 | | result.M22 = Fixed64.One; |
| | 13 | 35 | | result.M23 = Fixed64.Zero; |
| | 13 | 36 | | result.M24 = Fixed64.Zero; |
| | 13 | 37 | | result.M31 = Fixed64.Zero; |
| | 13 | 38 | | result.M32 = Fixed64.Zero; |
| | 13 | 39 | | result.M33 = Fixed64.One; |
| | 13 | 40 | | result.M34 = Fixed64.Zero; |
| | 13 | 41 | | result.M41 = position.X; |
| | 13 | 42 | | result.M42 = position.Y; |
| | 13 | 43 | | result.M43 = position.Z; |
| | 13 | 44 | | result.M44 = Fixed64.One; |
| | 13 | 45 | | return result; |
| | | 46 | | } |
| | | 47 | | |
| | | 48 | | /// <summary> |
| | | 49 | | /// Creates a translation matrix from the specified coordinates. |
| | | 50 | | /// </summary> |
| | | 51 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 52 | | public static Fixed4x4 CreateTranslation(Fixed64 x, Fixed64 y, Fixed64 z) => |
| | 1 | 53 | | CreateTranslation(new Vector3d(x, y, z)); |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// Creates a rotation matrix from a quaternion. |
| | | 57 | | /// </summary> |
| | | 58 | | /// <remarks>Quaternion magnitude does not affect the result; zero represents identity.</remarks> |
| | | 59 | | /// <param name="rotation">The quaternion representing the rotation.</param> |
| | | 60 | | /// <returns>A 4x4 matrix representing the rotation.</returns> |
| | | 61 | | public static Fixed4x4 CreateRotation(FixedQuaternion rotation) |
| | | 62 | | { |
| | 17 | 63 | | Fixed3x3 rotationMatrix = rotation.ToMatrix3x3(); |
| | | 64 | | |
| | 17 | 65 | | return new Fixed4x4( |
| | 17 | 66 | | rotationMatrix.M11, rotationMatrix.M12, rotationMatrix.M13, Fixed64.Zero, |
| | 17 | 67 | | rotationMatrix.M21, rotationMatrix.M22, rotationMatrix.M23, Fixed64.Zero, |
| | 17 | 68 | | rotationMatrix.M31, rotationMatrix.M32, rotationMatrix.M33, Fixed64.Zero, |
| | 17 | 69 | | Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One); |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | /// <summary> |
| | | 73 | | /// Creates a rotation matrix around the X axis. |
| | | 74 | | /// </summary> |
| | | 75 | | public static Fixed4x4 CreateRotationX(Fixed64 angle) => |
| | 6 | 76 | | FromRotationMatrix(Fixed3x3.CreateRotationX(angle)); |
| | | 77 | | |
| | | 78 | | /// <summary> |
| | | 79 | | /// Creates a rotation matrix around the Y axis. |
| | | 80 | | /// </summary> |
| | | 81 | | public static Fixed4x4 CreateRotationY(Fixed64 angle) => |
| | 6 | 82 | | FromRotationMatrix(Fixed3x3.CreateRotationY(angle)); |
| | | 83 | | |
| | | 84 | | /// <summary> |
| | | 85 | | /// Creates a rotation matrix around the Z axis. |
| | | 86 | | /// </summary> |
| | | 87 | | public static Fixed4x4 CreateRotationZ(Fixed64 angle) => |
| | 5 | 88 | | FromRotationMatrix(Fixed3x3.CreateRotationZ(angle)); |
| | | 89 | | |
| | | 90 | | /// <summary> |
| | | 91 | | /// Creates a rotation matrix from an axis and angle. |
| | | 92 | | /// </summary> |
| | | 93 | | public static Fixed4x4 CreateFromAxisAngle(Vector3d axis, Fixed64 angle) => |
| | 1 | 94 | | CreateRotation(FixedQuaternion.FromAxisAngle(axis, angle)); |
| | | 95 | | |
| | | 96 | | /// <summary> |
| | | 97 | | /// Creates a rotation matrix from pitch, yaw, and roll angles in radians. |
| | | 98 | | /// </summary> |
| | | 99 | | public static Fixed4x4 CreateFromEulerAngles(Fixed64 pitch, Fixed64 yaw, Fixed64 roll) => |
| | 1 | 100 | | CreateRotation(FixedQuaternion.FromEulerAngles(pitch, yaw, roll)); |
| | | 101 | | |
| | | 102 | | /// <summary> |
| | | 103 | | /// Creates a scale matrix from a 3-dimensional vector. |
| | | 104 | | /// </summary> |
| | | 105 | | /// <param name="scale">The vector representing the scale along each axis.</param> |
| | | 106 | | /// <returns>A 4x4 matrix representing the scale transformation.</returns> |
| | | 107 | | public static Fixed4x4 CreateScale(Vector3d scale) => |
| | 34 | 108 | | new( |
| | 34 | 109 | | scale.X, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 34 | 110 | | Fixed64.Zero, scale.Y, Fixed64.Zero, Fixed64.Zero, |
| | 34 | 111 | | Fixed64.Zero, Fixed64.Zero, scale.Z, Fixed64.Zero, |
| | 34 | 112 | | Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One); |
| | | 113 | | |
| | | 114 | | /// <summary> |
| | | 115 | | /// Creates a uniform scale matrix. |
| | | 116 | | /// </summary> |
| | | 117 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 1 | 118 | | public static Fixed4x4 CreateScale(Fixed64 scale) => CreateScale(new Vector3d(scale, scale, scale)); |
| | | 119 | | |
| | | 120 | | /// <summary> |
| | | 121 | | /// Creates a non-uniform scale matrix from individual scale components. |
| | | 122 | | /// </summary> |
| | | 123 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 1 | 124 | | public static Fixed4x4 CreateScale(Fixed64 x, Fixed64 y, Fixed64 z) => CreateScale(new Vector3d(x, y, z)); |
| | | 125 | | |
| | | 126 | | /// <summary> |
| | | 127 | | /// Creates a view matrix looking from a camera position toward a target. |
| | | 128 | | /// </summary> |
| | | 129 | | /// <remarks> |
| | | 130 | | /// The view direction is expressed in FixedMathSharp's canonical <c>+Z</c>-forward basis and |
| | | 131 | | /// the resulting matrix follows the row-vector convention used by <see cref="TransformPoint(Fixed4x4, Vector3d)"/>. |
| | | 132 | | /// Convert engine-specific camera conventions at adapter boundaries. |
| | | 133 | | /// </remarks> |
| | | 134 | | public static Fixed4x4 CreateLookAt(Vector3d cameraPosition, Vector3d cameraTarget, Vector3d cameraUpVector) |
| | | 135 | | { |
| | 4 | 136 | | Vector3d forward = cameraTarget - cameraPosition; |
| | | 137 | | |
| | 4 | 138 | | if (forward.MagnitudeSquared == Fixed64.Zero) |
| | 1 | 139 | | throw new ArgumentException("Camera position and target must be different."); |
| | | 140 | | |
| | 3 | 141 | | forward = forward.NormalizeInPlace(); |
| | 3 | 142 | | Vector3d right = Vector3d.Cross(cameraUpVector, forward); |
| | | 143 | | |
| | 3 | 144 | | if (right.MagnitudeSquared == Fixed64.Zero) |
| | 1 | 145 | | throw new ArgumentException("Camera up vector must not be parallel to the view direction."); |
| | | 146 | | |
| | 2 | 147 | | right = right.NormalizeInPlace(); |
| | 2 | 148 | | Vector3d up = Vector3d.Cross(forward, right).NormalizeInPlace(); |
| | | 149 | | |
| | 2 | 150 | | return new Fixed4x4( |
| | 2 | 151 | | right.X, right.Y, right.Z, Fixed64.Zero, |
| | 2 | 152 | | up.X, up.Y, up.Z, Fixed64.Zero, |
| | 2 | 153 | | forward.X, forward.Y, forward.Z, Fixed64.Zero, |
| | 2 | 154 | | -Vector3d.Dot(right, cameraPosition), |
| | 2 | 155 | | -Vector3d.Dot(up, cameraPosition), |
| | 2 | 156 | | -Vector3d.Dot(forward, cameraPosition), |
| | 2 | 157 | | Fixed64.One); |
| | | 158 | | } |
| | | 159 | | |
| | | 160 | | /// <summary> |
| | | 161 | | /// Creates an orthographic projection matrix centered on the origin. |
| | | 162 | | /// </summary> |
| | | 163 | | public static Fixed4x4 CreateOrthographic(Fixed64 width, Fixed64 height, Fixed64 zNearPlane, Fixed64 zFarPlane) |
| | | 164 | | { |
| | 5 | 165 | | if (width <= Fixed64.Zero) |
| | 1 | 166 | | throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than zero."); |
| | | 167 | | |
| | 4 | 168 | | if (height <= Fixed64.Zero) |
| | 1 | 169 | | throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than zero."); |
| | | 170 | | |
| | 3 | 171 | | Fixed64 halfWidth = width * Fixed64.Half; |
| | 3 | 172 | | Fixed64 halfHeight = height * Fixed64.Half; |
| | 3 | 173 | | return CreateOrthographicOffCenter(-halfWidth, halfWidth, -halfHeight, halfHeight, zNearPlane, zFarPlane); |
| | | 174 | | } |
| | | 175 | | |
| | | 176 | | /// <summary> |
| | | 177 | | /// Creates an off-center orthographic projection matrix. |
| | | 178 | | /// </summary> |
| | | 179 | | public static Fixed4x4 CreateOrthographicOffCenter( |
| | | 180 | | Fixed64 left, |
| | | 181 | | Fixed64 right, |
| | | 182 | | Fixed64 bottom, |
| | | 183 | | Fixed64 top, |
| | | 184 | | Fixed64 zNearPlane, |
| | | 185 | | Fixed64 zFarPlane) |
| | | 186 | | { |
| | 8 | 187 | | if (left == right) |
| | 1 | 188 | | throw new ArgumentOutOfRangeException(nameof(right), "Right must be different from left."); |
| | | 189 | | |
| | 7 | 190 | | if (bottom == top) |
| | 1 | 191 | | throw new ArgumentOutOfRangeException(nameof(top), "Top must be different from bottom."); |
| | | 192 | | |
| | 6 | 193 | | ValidateDepthRange(zNearPlane, zFarPlane); |
| | | 194 | | |
| | 2 | 195 | | Fixed64 width = right - left; |
| | 2 | 196 | | Fixed64 height = top - bottom; |
| | 2 | 197 | | Fixed64 depth = zFarPlane - zNearPlane; |
| | | 198 | | |
| | 2 | 199 | | return new Fixed4x4( |
| | 2 | 200 | | Fixed64.Two / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 2 | 201 | | Fixed64.Zero, Fixed64.Two / height, Fixed64.Zero, Fixed64.Zero, |
| | 2 | 202 | | Fixed64.Zero, Fixed64.Zero, Fixed64.One / depth, Fixed64.Zero, |
| | 2 | 203 | | (left + right) / (left - right), |
| | 2 | 204 | | (top + bottom) / (bottom - top), |
| | 2 | 205 | | -zNearPlane / depth, |
| | 2 | 206 | | Fixed64.One); |
| | | 207 | | } |
| | | 208 | | |
| | | 209 | | /// <summary> |
| | | 210 | | /// Creates a perspective projection matrix centered on the near plane. |
| | | 211 | | /// </summary> |
| | | 212 | | public static Fixed4x4 CreatePerspective( |
| | | 213 | | Fixed64 width, |
| | | 214 | | Fixed64 height, |
| | | 215 | | Fixed64 nearPlaneDistance, |
| | | 216 | | Fixed64 farPlaneDistance) |
| | | 217 | | { |
| | 5 | 218 | | if (width <= Fixed64.Zero) |
| | 1 | 219 | | throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than zero."); |
| | | 220 | | |
| | 4 | 221 | | if (height <= Fixed64.Zero) |
| | 1 | 222 | | throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than zero."); |
| | | 223 | | |
| | 3 | 224 | | ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance); |
| | | 225 | | |
| | 1 | 226 | | Fixed64 depth = farPlaneDistance - nearPlaneDistance; |
| | 1 | 227 | | Fixed64 twoNear = Fixed64.Two * nearPlaneDistance; |
| | | 228 | | |
| | 1 | 229 | | return new Fixed4x4( |
| | 1 | 230 | | twoNear / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 1 | 231 | | Fixed64.Zero, twoNear / height, Fixed64.Zero, Fixed64.Zero, |
| | 1 | 232 | | Fixed64.Zero, Fixed64.Zero, farPlaneDistance / depth, Fixed64.One, |
| | 1 | 233 | | Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero); |
| | | 234 | | } |
| | | 235 | | |
| | | 236 | | /// <summary> |
| | | 237 | | /// Creates a perspective projection matrix from a vertical field of view. |
| | | 238 | | /// </summary> |
| | | 239 | | public static Fixed4x4 CreatePerspectiveFieldOfView( |
| | | 240 | | Fixed64 fieldOfView, |
| | | 241 | | Fixed64 aspectRatio, |
| | | 242 | | Fixed64 nearPlaneDistance, |
| | | 243 | | Fixed64 farPlaneDistance) |
| | | 244 | | { |
| | 7 | 245 | | if (fieldOfView <= Fixed64.Zero || fieldOfView >= Fixed64.Pi) |
| | 2 | 246 | | throw new ArgumentOutOfRangeException(nameof(fieldOfView), "Field of view must be greater than zero and less |
| | | 247 | | |
| | 5 | 248 | | if (aspectRatio <= Fixed64.Zero) |
| | 1 | 249 | | throw new ArgumentOutOfRangeException(nameof(aspectRatio), "Aspect ratio must be greater than zero."); |
| | | 250 | | |
| | 4 | 251 | | ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance); |
| | | 252 | | |
| | 2 | 253 | | Fixed64 yScale = Fixed64.One / FixedMath.Tan(fieldOfView * Fixed64.Half); |
| | 2 | 254 | | Fixed64 xScale = yScale / aspectRatio; |
| | 2 | 255 | | Fixed64 depth = farPlaneDistance - nearPlaneDistance; |
| | | 256 | | |
| | 2 | 257 | | return new Fixed4x4( |
| | 2 | 258 | | xScale, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 2 | 259 | | Fixed64.Zero, yScale, Fixed64.Zero, Fixed64.Zero, |
| | 2 | 260 | | Fixed64.Zero, Fixed64.Zero, farPlaneDistance / depth, Fixed64.One, |
| | 2 | 261 | | Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero); |
| | | 262 | | } |
| | | 263 | | |
| | | 264 | | /// <summary> |
| | | 265 | | /// Creates an off-center perspective projection matrix. |
| | | 266 | | /// </summary> |
| | | 267 | | public static Fixed4x4 CreatePerspectiveOffCenter( |
| | | 268 | | Fixed64 left, |
| | | 269 | | Fixed64 right, |
| | | 270 | | Fixed64 bottom, |
| | | 271 | | Fixed64 top, |
| | | 272 | | Fixed64 nearPlaneDistance, |
| | | 273 | | Fixed64 farPlaneDistance) |
| | | 274 | | { |
| | 5 | 275 | | if (left == right) |
| | 1 | 276 | | throw new ArgumentOutOfRangeException(nameof(right), "Right must be different from left."); |
| | | 277 | | |
| | 4 | 278 | | if (bottom == top) |
| | 1 | 279 | | throw new ArgumentOutOfRangeException(nameof(top), "Top must be different from bottom."); |
| | | 280 | | |
| | 3 | 281 | | ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance); |
| | | 282 | | |
| | 1 | 283 | | Fixed64 width = right - left; |
| | 1 | 284 | | Fixed64 height = top - bottom; |
| | 1 | 285 | | Fixed64 depth = farPlaneDistance - nearPlaneDistance; |
| | 1 | 286 | | Fixed64 twoNear = Fixed64.Two * nearPlaneDistance; |
| | | 287 | | |
| | 1 | 288 | | return new Fixed4x4( |
| | 1 | 289 | | twoNear / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 1 | 290 | | Fixed64.Zero, twoNear / height, Fixed64.Zero, Fixed64.Zero, |
| | 1 | 291 | | (left + right) / (left - right), |
| | 1 | 292 | | (top + bottom) / (bottom - top), |
| | 1 | 293 | | farPlaneDistance / depth, |
| | 1 | 294 | | Fixed64.One, |
| | 1 | 295 | | Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero); |
| | | 296 | | } |
| | | 297 | | |
| | | 298 | | /// <summary> |
| | | 299 | | /// Creates a world matrix from a position and orientation basis. |
| | | 300 | | /// </summary> |
| | | 301 | | /// <remarks> |
| | | 302 | | /// The <paramref name="forward"/> and <paramref name="up"/> vectors are semantic basis vectors |
| | | 303 | | /// in FixedMathSharp's canonical coordinate space. The returned matrix stores right, up, and |
| | | 304 | | /// forward basis rows for row-vector transforms. |
| | | 305 | | /// </remarks> |
| | | 306 | | public static Fixed4x4 CreateWorld(Vector3d position, Vector3d forward, Vector3d up) |
| | | 307 | | { |
| | 3 | 308 | | if (forward.MagnitudeSquared == Fixed64.Zero) |
| | 1 | 309 | | throw new ArgumentException("Forward vector must be non-zero."); |
| | | 310 | | |
| | 2 | 311 | | forward = forward.NormalizeInPlace(); |
| | 2 | 312 | | Vector3d right = Vector3d.Cross(up, forward); |
| | | 313 | | |
| | 2 | 314 | | if (right.MagnitudeSquared == Fixed64.Zero) |
| | 1 | 315 | | throw new ArgumentException("Up vector must not be parallel to forward."); |
| | | 316 | | |
| | 1 | 317 | | right = right.NormalizeInPlace(); |
| | 1 | 318 | | up = Vector3d.Cross(forward, right).NormalizeInPlace(); |
| | | 319 | | |
| | 1 | 320 | | return new Fixed4x4( |
| | 1 | 321 | | right.X, right.Y, right.Z, Fixed64.Zero, |
| | 1 | 322 | | up.X, up.Y, up.Z, Fixed64.Zero, |
| | 1 | 323 | | forward.X, forward.Y, forward.Z, Fixed64.Zero, |
| | 1 | 324 | | position.X, position.Y, position.Z, Fixed64.One); |
| | | 325 | | } |
| | | 326 | | |
| | | 327 | | /// <summary> |
| | | 328 | | /// Constructs a transformation matrix from translation, scale, and rotation. |
| | | 329 | | /// This method ensures that the rotation is properly normalized, applies the scale to the |
| | | 330 | | /// rotational basis, and sets the translation component separately. |
| | | 331 | | /// </summary> |
| | | 332 | | /// <remarks> |
| | | 333 | | /// - Quaternion magnitude does not affect the rotation matrix; zero represents identity. |
| | | 334 | | /// - Applies non-uniform scaling to the rotation before setting translation. |
| | | 335 | | /// - Preferred when ensuring transformations remain mathematically correct. |
| | | 336 | | /// - For explicit matrix-composition order, see <see cref="ScaleRotateTranslate"/>. |
| | | 337 | | /// </remarks> |
| | | 338 | | /// <param name="translation">The translation vector.</param> |
| | | 339 | | /// <param name="scale">The scale vector.</param> |
| | | 340 | | /// <param name="rotation">The rotation quaternion.</param> |
| | | 341 | | /// <returns>A transformation matrix incorporating translation, rotation, and scale.</returns> |
| | | 342 | | public static Fixed4x4 CreateTransform(Vector3d translation, FixedQuaternion rotation, Vector3d scale) => |
| | 28555 | 343 | | CreateTransform(translation, rotation.ToMatrix3x3(), scale); |
| | | 344 | | |
| | | 345 | | private static Fixed4x4 CreateTransform(Vector3d translation, Fixed3x3 rotation, Vector3d scale) |
| | | 346 | | { |
| | 28585 | 347 | | return new Fixed4x4( |
| | 28585 | 348 | | rotation.M11 * scale.X, rotation.M12 * scale.X, rotation.M13 * scale.X, Fixed64.Zero, |
| | 28585 | 349 | | rotation.M21 * scale.Y, rotation.M22 * scale.Y, rotation.M23 * scale.Y, Fixed64.Zero, |
| | 28585 | 350 | | rotation.M31 * scale.Z, rotation.M32 * scale.Z, rotation.M33 * scale.Z, Fixed64.Zero, |
| | 28585 | 351 | | translation.X, translation.Y, translation.Z, Fixed64.One); |
| | | 352 | | } |
| | | 353 | | |
| | | 354 | | /// <summary> |
| | | 355 | | /// Constructs a transformation matrix from translation, rotation, and scale by multiplying |
| | | 356 | | /// separate matrices in the order: Scale * Rotation * Translation. |
| | | 357 | | /// </summary> |
| | | 358 | | /// <remarks> |
| | | 359 | | /// - This method directly multiplies the scale, rotation, and translation matrices. |
| | | 360 | | /// - Ensures that scale is applied first to preserve correct axis scaling. |
| | | 361 | | /// - Then rotation is applied so that rotation is not affected by non-uniform scaling. |
| | | 362 | | /// - Finally, translation moves the object to its correct world position. |
| | | 363 | | /// </remarks> |
| | | 364 | | public static Fixed4x4 ScaleRotateTranslate(Vector3d translation, FixedQuaternion rotation, Vector3d scale) |
| | | 365 | | { |
| | 9 | 366 | | return CreateTransform(translation, rotation, scale); |
| | | 367 | | } |
| | | 368 | | |
| | | 369 | | /// <summary> |
| | | 370 | | /// Constructs a transformation matrix from translation, rotation, and scale by multiplying |
| | | 371 | | /// matrices in the order: Translation * Rotation * Scale (T * R * S). |
| | | 372 | | /// </summary> |
| | | 373 | | /// <remarks> |
| | | 374 | | /// - Use this method when transformations need to be applied **relative to an object's local origin**. |
| | | 375 | | /// - Example use cases include **animation systems**, **hierarchical transformations**, and **UI transformations**. |
| | | 376 | | /// - If you need to apply world-space transformations, use |
| | | 377 | | /// <see cref="CreateTransform(Vector3d, FixedQuaternion, Vector3d)"/> instead. |
| | | 378 | | /// - Quaternion magnitude does not affect the rotation matrix; zero represents identity. |
| | | 379 | | /// </remarks> |
| | | 380 | | public static Fixed4x4 TranslateRotateScale(Vector3d translation, FixedQuaternion rotation, Vector3d scale) |
| | | 381 | | { |
| | 5 | 382 | | Fixed3x3 rotationMatrix = rotation.ToMatrix3x3(); |
| | | 383 | | |
| | 5 | 384 | | return new Fixed4x4( |
| | 5 | 385 | | rotationMatrix.M11 * scale.X, rotationMatrix.M12 * scale.Y, rotationMatrix.M13 * scale.Z, Fixed64.Zero, |
| | 5 | 386 | | rotationMatrix.M21 * scale.X, rotationMatrix.M22 * scale.Y, rotationMatrix.M23 * scale.Z, Fixed64.Zero, |
| | 5 | 387 | | rotationMatrix.M31 * scale.X, rotationMatrix.M32 * scale.Y, rotationMatrix.M33 * scale.Z, Fixed64.Zero, |
| | 5 | 388 | | (translation.X * rotationMatrix.M11 + translation.Y * rotationMatrix.M21 + translation.Z * rotationMatrix.M3 |
| | 5 | 389 | | (translation.X * rotationMatrix.M12 + translation.Y * rotationMatrix.M22 + translation.Z * rotationMatrix.M3 |
| | 5 | 390 | | (translation.X * rotationMatrix.M13 + translation.Y * rotationMatrix.M23 + translation.Z * rotationMatrix.M3 |
| | 5 | 391 | | Fixed64.One); |
| | | 392 | | } |
| | | 393 | | |
| | | 394 | | #endregion |
| | | 395 | | } |