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