| | | 1 | | //======================================================================= |
| | | 2 | | // TriangleShellMassProperties.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 | | |
| | | 10 | | using FixedMathSharp; |
| | | 11 | | using FixedMathSharp.Geometry; |
| | | 12 | | |
| | | 13 | | namespace Gravitas.Colliders; |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// Owns wide uniform thin-shell integration for indexed triangle surfaces. |
| | | 17 | | /// </summary> |
| | | 18 | | internal static class TriangleShellMassProperties |
| | | 19 | | { |
| | 2 | 20 | | private static readonly Signed192 Three = Signed192.Signed(3); |
| | 2 | 21 | | private static readonly Signed192 Six = Signed192.Signed(6); |
| | 2 | 22 | | private static readonly Signed192 Twelve = Signed192.Signed(12); |
| | | 23 | | |
| | | 24 | | internal static bool TryCreateUniformShell( |
| | | 25 | | ReadOnlySpan<Vector3d> vertices, |
| | | 26 | | ReadOnlySpan<int> triangleIndices, |
| | | 27 | | out ExactMassWeight surfaceWeight, |
| | | 28 | | out Vector3d centerOfMass, |
| | | 29 | | out Fixed3x3 unitMassInertiaTensor) |
| | | 30 | | { |
| | 462 | 31 | | if (triangleIndices.Length % FixedTriangle.VertexCount != 0) |
| | | 32 | | { |
| | 1 | 33 | | throw new ArgumentException( |
| | 1 | 34 | | "Triangle indices must contain complete index triplets.", |
| | 1 | 35 | | nameof(triangleIndices)); |
| | | 36 | | } |
| | | 37 | | |
| | 461 | 38 | | Signed576 totalWeight = default; |
| | 461 | 39 | | Signed576 firstMomentX = default; |
| | 461 | 40 | | Signed576 firstMomentY = default; |
| | 461 | 41 | | Signed576 firstMomentZ = default; |
| | 461 | 42 | | Signed576 secondMomentX = default; |
| | 461 | 43 | | Signed576 secondMomentY = default; |
| | 461 | 44 | | Signed576 secondMomentZ = default; |
| | 461 | 45 | | Signed576 productMomentXY = default; |
| | 461 | 46 | | Signed576 productMomentXZ = default; |
| | 461 | 47 | | Signed576 productMomentYZ = default; |
| | 324822 | 48 | | for (int i = 0; i < triangleIndices.Length; i += FixedTriangle.VertexCount) |
| | | 49 | | { |
| | 161953 | 50 | | GetTriangle( |
| | 161953 | 51 | | vertices, |
| | 161953 | 52 | | triangleIndices, |
| | 161953 | 53 | | i, |
| | 161953 | 54 | | out FixedTriangle triangle); |
| | 161950 | 55 | | triangle.GetExactNormal( |
| | 161950 | 56 | | out _, |
| | 161950 | 57 | | out _, |
| | 161950 | 58 | | out _, |
| | 161950 | 59 | | out Signed320 squaredDoubleArea); |
| | 161950 | 60 | | ExactMassWeight weight = |
| | 161950 | 61 | | ExactMassProperties.CreateTriangleAreaWeight( |
| | 161950 | 62 | | squaredDoubleArea); |
| | 161950 | 63 | | Signed320 weightNumerator = weight.Numerator; |
| | 161950 | 64 | | totalWeight = WideArithmetic.AddSigned576( |
| | 161950 | 65 | | totalWeight, |
| | 161950 | 66 | | Signed576.ExtendValue(weightNumerator)); |
| | 161950 | 67 | | firstMomentX = AddWeightedVertexSum( |
| | 161950 | 68 | | firstMomentX, |
| | 161950 | 69 | | triangle.A.X, |
| | 161950 | 70 | | triangle.B.X, |
| | 161950 | 71 | | triangle.C.X, |
| | 161950 | 72 | | weightNumerator); |
| | 161950 | 73 | | firstMomentY = AddWeightedVertexSum( |
| | 161950 | 74 | | firstMomentY, |
| | 161950 | 75 | | triangle.A.Y, |
| | 161950 | 76 | | triangle.B.Y, |
| | 161950 | 77 | | triangle.C.Y, |
| | 161950 | 78 | | weightNumerator); |
| | 161950 | 79 | | firstMomentZ = AddWeightedVertexSum( |
| | 161950 | 80 | | firstMomentZ, |
| | 161950 | 81 | | triangle.A.Z, |
| | 161950 | 82 | | triangle.B.Z, |
| | 161950 | 83 | | triangle.C.Z, |
| | 161950 | 84 | | weightNumerator); |
| | 161950 | 85 | | GetProductSums( |
| | 161950 | 86 | | triangle, |
| | 161950 | 87 | | out Signed320 x2, |
| | 161950 | 88 | | out Signed320 y2, |
| | 161950 | 89 | | out Signed320 z2, |
| | 161950 | 90 | | out Signed320 xy, |
| | 161950 | 91 | | out Signed320 xz, |
| | 161950 | 92 | | out Signed320 yz); |
| | 161950 | 93 | | secondMomentX = AddWeighted( |
| | 161950 | 94 | | secondMomentX, |
| | 161950 | 95 | | x2, |
| | 161950 | 96 | | weightNumerator); |
| | 161950 | 97 | | secondMomentY = AddWeighted( |
| | 161950 | 98 | | secondMomentY, |
| | 161950 | 99 | | y2, |
| | 161950 | 100 | | weightNumerator); |
| | 161950 | 101 | | secondMomentZ = AddWeighted( |
| | 161950 | 102 | | secondMomentZ, |
| | 161950 | 103 | | z2, |
| | 161950 | 104 | | weightNumerator); |
| | 161950 | 105 | | productMomentXY = AddWeighted( |
| | 161950 | 106 | | productMomentXY, |
| | 161950 | 107 | | xy, |
| | 161950 | 108 | | weightNumerator); |
| | 161950 | 109 | | productMomentXZ = AddWeighted( |
| | 161950 | 110 | | productMomentXZ, |
| | 161950 | 111 | | xz, |
| | 161950 | 112 | | weightNumerator); |
| | 161950 | 113 | | productMomentYZ = AddWeighted( |
| | 161950 | 114 | | productMomentYZ, |
| | 161950 | 115 | | yz, |
| | 161950 | 116 | | weightNumerator); |
| | | 117 | | } |
| | | 118 | | |
| | 458 | 119 | | if (totalWeight.IsZero) |
| | | 120 | | { |
| | 1 | 121 | | surfaceWeight = default; |
| | 1 | 122 | | centerOfMass = default; |
| | 1 | 123 | | unitMassInertiaTensor = default; |
| | 1 | 124 | | return false; |
| | | 125 | | } |
| | | 126 | | |
| | 457 | 127 | | surfaceWeight = new ExactMassWeight( |
| | 457 | 128 | | Signed320.NarrowValue(totalWeight)); |
| | 457 | 129 | | Signed576 centerDenominator = |
| | 457 | 130 | | WideArithmetic.MultiplySigned576( |
| | 457 | 131 | | totalWeight, |
| | 457 | 132 | | Three); |
| | 457 | 133 | | _ = Fixed64.TryGetSignedRawRatio( |
| | 457 | 134 | | firstMomentX, |
| | 457 | 135 | | centerDenominator, |
| | 457 | 136 | | out Fixed64 centerX); |
| | 457 | 137 | | _ = Fixed64.TryGetSignedRawRatio( |
| | 457 | 138 | | firstMomentY, |
| | 457 | 139 | | centerDenominator, |
| | 457 | 140 | | out Fixed64 centerY); |
| | 457 | 141 | | _ = Fixed64.TryGetSignedRawRatio( |
| | 457 | 142 | | firstMomentZ, |
| | 457 | 143 | | centerDenominator, |
| | 457 | 144 | | out Fixed64 centerZ); |
| | 457 | 145 | | centerOfMass = new Vector3d( |
| | 457 | 146 | | centerX, |
| | 457 | 147 | | centerY, |
| | 457 | 148 | | centerZ); |
| | | 149 | | |
| | 457 | 150 | | Signed576 centeredX = TranslateSquaredMoment( |
| | 457 | 151 | | secondMomentX, |
| | 457 | 152 | | firstMomentX, |
| | 457 | 153 | | totalWeight, |
| | 457 | 154 | | centerX); |
| | 457 | 155 | | Signed576 centeredY = TranslateSquaredMoment( |
| | 457 | 156 | | secondMomentY, |
| | 457 | 157 | | firstMomentY, |
| | 457 | 158 | | totalWeight, |
| | 457 | 159 | | centerY); |
| | 457 | 160 | | Signed576 centeredZ = TranslateSquaredMoment( |
| | 457 | 161 | | secondMomentZ, |
| | 457 | 162 | | firstMomentZ, |
| | 457 | 163 | | totalWeight, |
| | 457 | 164 | | centerZ); |
| | 457 | 165 | | Signed576 centeredXY = TranslateProductMoment( |
| | 457 | 166 | | productMomentXY, |
| | 457 | 167 | | firstMomentX, |
| | 457 | 168 | | firstMomentY, |
| | 457 | 169 | | totalWeight, |
| | 457 | 170 | | centerX, |
| | 457 | 171 | | centerY); |
| | 457 | 172 | | Signed576 centeredXZ = TranslateProductMoment( |
| | 457 | 173 | | productMomentXZ, |
| | 457 | 174 | | firstMomentX, |
| | 457 | 175 | | firstMomentZ, |
| | 457 | 176 | | totalWeight, |
| | 457 | 177 | | centerX, |
| | 457 | 178 | | centerZ); |
| | 457 | 179 | | Signed576 centeredYZ = TranslateProductMoment( |
| | 457 | 180 | | productMomentYZ, |
| | 457 | 181 | | firstMomentY, |
| | 457 | 182 | | firstMomentZ, |
| | 457 | 183 | | totalWeight, |
| | 457 | 184 | | centerY, |
| | 457 | 185 | | centerZ); |
| | 457 | 186 | | Signed576 inertiaXX = |
| | 457 | 187 | | WideArithmetic.AddSigned576(centeredY, centeredZ); |
| | 457 | 188 | | Signed576 inertiaYY = |
| | 457 | 189 | | WideArithmetic.AddSigned576(centeredX, centeredZ); |
| | 457 | 190 | | Signed576 inertiaZZ = |
| | 457 | 191 | | WideArithmetic.AddSigned576(centeredX, centeredY); |
| | | 192 | | |
| | 457 | 193 | | Signed576 diagonalDenominator = |
| | 457 | 194 | | WideArithmetic.MultiplySigned576( |
| | 457 | 195 | | totalWeight, |
| | 457 | 196 | | Signed192.One, |
| | 457 | 197 | | Six); |
| | 457 | 198 | | Signed576 productDenominator = |
| | 457 | 199 | | WideArithmetic.MultiplySigned576( |
| | 457 | 200 | | totalWeight, |
| | 457 | 201 | | Signed192.One, |
| | 457 | 202 | | Twelve); |
| | 457 | 203 | | bool representable = Fixed64.TryGetSignedRawRatio( |
| | 457 | 204 | | inertiaXX, |
| | 457 | 205 | | diagonalDenominator, |
| | 457 | 206 | | out Fixed64 tensorXX) |
| | 457 | 207 | | & Fixed64.TryGetSignedRawRatio( |
| | 457 | 208 | | inertiaYY, |
| | 457 | 209 | | diagonalDenominator, |
| | 457 | 210 | | out Fixed64 tensorYY) |
| | 457 | 211 | | & Fixed64.TryGetSignedRawRatio( |
| | 457 | 212 | | inertiaZZ, |
| | 457 | 213 | | diagonalDenominator, |
| | 457 | 214 | | out Fixed64 tensorZZ) |
| | 457 | 215 | | & Fixed64.TryGetSignedRawRatio( |
| | 457 | 216 | | WideArithmetic.SubtractSigned576(default, centeredXY), |
| | 457 | 217 | | productDenominator, |
| | 457 | 218 | | out Fixed64 tensorXY) |
| | 457 | 219 | | & Fixed64.TryGetSignedRawRatio( |
| | 457 | 220 | | WideArithmetic.SubtractSigned576(default, centeredXZ), |
| | 457 | 221 | | productDenominator, |
| | 457 | 222 | | out Fixed64 tensorXZ) |
| | 457 | 223 | | & Fixed64.TryGetSignedRawRatio( |
| | 457 | 224 | | WideArithmetic.SubtractSigned576(default, centeredYZ), |
| | 457 | 225 | | productDenominator, |
| | 457 | 226 | | out Fixed64 tensorYZ); |
| | 457 | 227 | | if (!representable) |
| | | 228 | | { |
| | 27 | 229 | | surfaceWeight = default; |
| | 27 | 230 | | centerOfMass = default; |
| | 27 | 231 | | unitMassInertiaTensor = default; |
| | 27 | 232 | | return false; |
| | | 233 | | } |
| | | 234 | | |
| | 430 | 235 | | unitMassInertiaTensor = new Fixed3x3( |
| | 430 | 236 | | tensorXX, tensorXY, tensorXZ, |
| | 430 | 237 | | tensorXY, tensorYY, tensorYZ, |
| | 430 | 238 | | tensorXZ, tensorYZ, tensorZZ); |
| | 430 | 239 | | return true; |
| | | 240 | | } |
| | | 241 | | |
| | | 242 | | private static Signed576 AddWeightedVertexSum( |
| | | 243 | | Signed576 total, |
| | | 244 | | Fixed64 first, |
| | | 245 | | Fixed64 second, |
| | | 246 | | Fixed64 third, |
| | | 247 | | Signed320 weight) |
| | | 248 | | { |
| | 485850 | 249 | | Signed320 sum = WideArithmetic.AddSigned320( |
| | 485850 | 250 | | WideArithmetic.AddSigned320( |
| | 485850 | 251 | | Signed320.ExtendValue(Signed192.Raw(first)), |
| | 485850 | 252 | | Signed320.ExtendValue(Signed192.Raw(second))), |
| | 485850 | 253 | | Signed320.ExtendValue(Signed192.Raw(third))); |
| | 485850 | 254 | | return AddWeighted(total, sum, weight); |
| | | 255 | | } |
| | | 256 | | |
| | | 257 | | private static Signed576 AddWeighted( |
| | | 258 | | Signed576 total, |
| | | 259 | | Signed320 value, |
| | | 260 | | Signed320 weight) => |
| | 1457550 | 261 | | WideArithmetic.AddSigned576( |
| | 1457550 | 262 | | total, |
| | 1457550 | 263 | | WideArithmetic.MultiplySigned320( |
| | 1457550 | 264 | | value, |
| | 1457550 | 265 | | weight)); |
| | | 266 | | |
| | | 267 | | private static void GetProductSums( |
| | | 268 | | FixedTriangle triangle, |
| | | 269 | | out Signed320 x2, |
| | | 270 | | out Signed320 y2, |
| | | 271 | | out Signed320 z2, |
| | | 272 | | out Signed320 xy, |
| | | 273 | | out Signed320 xz, |
| | | 274 | | out Signed320 yz) |
| | | 275 | | { |
| | 161950 | 276 | | Signed192 ax = Signed192.Raw(triangle.A.X); |
| | 161950 | 277 | | Signed192 ay = Signed192.Raw(triangle.A.Y); |
| | 161950 | 278 | | Signed192 az = Signed192.Raw(triangle.A.Z); |
| | 161950 | 279 | | Signed192 bx = Signed192.Raw(triangle.B.X); |
| | 161950 | 280 | | Signed192 by = Signed192.Raw(triangle.B.Y); |
| | 161950 | 281 | | Signed192 bz = Signed192.Raw(triangle.B.Z); |
| | 161950 | 282 | | Signed192 cx = Signed192.Raw(triangle.C.X); |
| | 161950 | 283 | | Signed192 cy = Signed192.Raw(triangle.C.Y); |
| | 161950 | 284 | | Signed192 cz = Signed192.Raw(triangle.C.Z); |
| | 161950 | 285 | | x2 = SquaredBarycentricSum(ax, bx, cx); |
| | 161950 | 286 | | y2 = SquaredBarycentricSum(ay, by, cy); |
| | 161950 | 287 | | z2 = SquaredBarycentricSum(az, bz, cz); |
| | 161950 | 288 | | xy = BarycentricCrossSum(ax, bx, cx, ay, by, cy); |
| | 161950 | 289 | | xz = BarycentricCrossSum(ax, bx, cx, az, bz, cz); |
| | 161950 | 290 | | yz = BarycentricCrossSum(ay, by, cy, az, bz, cz); |
| | 161950 | 291 | | } |
| | | 292 | | |
| | | 293 | | private static Signed320 SquaredBarycentricSum( |
| | | 294 | | Signed192 first, |
| | | 295 | | Signed192 second, |
| | | 296 | | Signed192 third) => |
| | 485850 | 297 | | WideArithmetic.AddSigned320( |
| | 485850 | 298 | | WideArithmetic.AddSigned320( |
| | 485850 | 299 | | WideArithmetic.AddSigned320( |
| | 485850 | 300 | | WideArithmetic.MultiplySigned192(first, first), |
| | 485850 | 301 | | WideArithmetic.MultiplySigned192(second, second)), |
| | 485850 | 302 | | WideArithmetic.AddSigned320( |
| | 485850 | 303 | | WideArithmetic.MultiplySigned192(third, third), |
| | 485850 | 304 | | WideArithmetic.MultiplySigned192(first, second))), |
| | 485850 | 305 | | WideArithmetic.AddSigned320( |
| | 485850 | 306 | | WideArithmetic.MultiplySigned192(first, third), |
| | 485850 | 307 | | WideArithmetic.MultiplySigned192(second, third))); |
| | | 308 | | |
| | | 309 | | private static Signed320 BarycentricCrossSum( |
| | | 310 | | Signed192 firstA, |
| | | 311 | | Signed192 firstB, |
| | | 312 | | Signed192 firstC, |
| | | 313 | | Signed192 secondA, |
| | | 314 | | Signed192 secondB, |
| | | 315 | | Signed192 secondC) |
| | | 316 | | { |
| | 485850 | 317 | | Signed192 firstSum = Signed192.NarrowProven( |
| | 485850 | 318 | | WideArithmetic.AddSigned320( |
| | 485850 | 319 | | WideArithmetic.AddSigned320( |
| | 485850 | 320 | | Signed320.ExtendValue(firstA), |
| | 485850 | 321 | | Signed320.ExtendValue(firstB)), |
| | 485850 | 322 | | Signed320.ExtendValue(firstC))); |
| | 485850 | 323 | | Signed192 secondSum = Signed192.NarrowProven( |
| | 485850 | 324 | | WideArithmetic.AddSigned320( |
| | 485850 | 325 | | WideArithmetic.AddSigned320( |
| | 485850 | 326 | | Signed320.ExtendValue(secondA), |
| | 485850 | 327 | | Signed320.ExtendValue(secondB)), |
| | 485850 | 328 | | Signed320.ExtendValue(secondC))); |
| | 485850 | 329 | | Signed320 matching = WideArithmetic.AddSigned320( |
| | 485850 | 330 | | WideArithmetic.AddSigned320( |
| | 485850 | 331 | | WideArithmetic.MultiplySigned192(firstA, secondA), |
| | 485850 | 332 | | WideArithmetic.MultiplySigned192(firstB, secondB)), |
| | 485850 | 333 | | WideArithmetic.MultiplySigned192(firstC, secondC)); |
| | 485850 | 334 | | return WideArithmetic.AddSigned320( |
| | 485850 | 335 | | WideArithmetic.MultiplySigned192( |
| | 485850 | 336 | | firstSum, |
| | 485850 | 337 | | secondSum), |
| | 485850 | 338 | | matching); |
| | | 339 | | } |
| | | 340 | | |
| | | 341 | | private static Signed576 TranslateSquaredMoment( |
| | | 342 | | Signed576 secondMoment, |
| | | 343 | | Signed576 firstMoment, |
| | | 344 | | Signed576 totalWeight, |
| | | 345 | | Fixed64 center) |
| | | 346 | | { |
| | 1371 | 347 | | Signed192 centerRaw = Signed192.Raw(center); |
| | 1371 | 348 | | Signed576 shifted = WideArithmetic.SubtractSigned576( |
| | 1371 | 349 | | secondMoment, |
| | 1371 | 350 | | WideArithmetic.MultiplySigned576( |
| | 1371 | 351 | | firstMoment, |
| | 1371 | 352 | | centerRaw, |
| | 1371 | 353 | | Signed192.Signed(4))); |
| | 1371 | 354 | | return WideArithmetic.AddSigned576( |
| | 1371 | 355 | | shifted, |
| | 1371 | 356 | | WideArithmetic.MultiplySigned576( |
| | 1371 | 357 | | totalWeight, |
| | 1371 | 358 | | centerRaw, |
| | 1371 | 359 | | centerRaw, |
| | 1371 | 360 | | Six)); |
| | | 361 | | } |
| | | 362 | | |
| | | 363 | | private static Signed576 TranslateProductMoment( |
| | | 364 | | Signed576 productMoment, |
| | | 365 | | Signed576 firstMoment, |
| | | 366 | | Signed576 secondMoment, |
| | | 367 | | Signed576 totalWeight, |
| | | 368 | | Fixed64 firstCenter, |
| | | 369 | | Fixed64 secondCenter) |
| | | 370 | | { |
| | 1371 | 371 | | Signed192 firstCenterRaw = |
| | 1371 | 372 | | Signed192.Raw(firstCenter); |
| | 1371 | 373 | | Signed192 secondCenterRaw = |
| | 1371 | 374 | | Signed192.Raw(secondCenter); |
| | 1371 | 375 | | Signed576 shifted = WideArithmetic.SubtractSigned576( |
| | 1371 | 376 | | productMoment, |
| | 1371 | 377 | | WideArithmetic.MultiplySigned576( |
| | 1371 | 378 | | secondMoment, |
| | 1371 | 379 | | firstCenterRaw, |
| | 1371 | 380 | | Signed192.Signed(4))); |
| | 1371 | 381 | | shifted = WideArithmetic.SubtractSigned576( |
| | 1371 | 382 | | shifted, |
| | 1371 | 383 | | WideArithmetic.MultiplySigned576( |
| | 1371 | 384 | | firstMoment, |
| | 1371 | 385 | | secondCenterRaw, |
| | 1371 | 386 | | Signed192.Signed(4))); |
| | 1371 | 387 | | return WideArithmetic.AddSigned576( |
| | 1371 | 388 | | shifted, |
| | 1371 | 389 | | WideArithmetic.MultiplySigned576( |
| | 1371 | 390 | | totalWeight, |
| | 1371 | 391 | | firstCenterRaw, |
| | 1371 | 392 | | secondCenterRaw, |
| | 1371 | 393 | | Twelve)); |
| | | 394 | | } |
| | | 395 | | |
| | | 396 | | private static void GetTriangle( |
| | | 397 | | ReadOnlySpan<Vector3d> vertices, |
| | | 398 | | ReadOnlySpan<int> triangleIndices, |
| | | 399 | | int index, |
| | | 400 | | out FixedTriangle triangle) |
| | | 401 | | { |
| | 161953 | 402 | | int first = triangleIndices[index]; |
| | 161953 | 403 | | int second = triangleIndices[index + 1]; |
| | 161953 | 404 | | int third = triangleIndices[index + 2]; |
| | 161953 | 405 | | if ((uint)first >= (uint)vertices.Length |
| | 161953 | 406 | | || (uint)second >= (uint)vertices.Length |
| | 161953 | 407 | | || (uint)third >= (uint)vertices.Length) |
| | | 408 | | { |
| | 3 | 409 | | throw new ArgumentOutOfRangeException( |
| | 3 | 410 | | nameof(triangleIndices), |
| | 3 | 411 | | "Triangle indices must reference supplied vertices."); |
| | | 412 | | } |
| | | 413 | | |
| | 161950 | 414 | | triangle = new FixedTriangle( |
| | 161950 | 415 | | vertices[first], |
| | 161950 | 416 | | vertices[second], |
| | 161950 | 417 | | vertices[third]); |
| | 161950 | 418 | | } |
| | | 419 | | } |