| | | 1 | | //======================================================================= |
| | | 2 | | // ConvexSweepQueryWorker.ConvexShape.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2026-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using FixedMathSharp; |
| | | 9 | | using FixedMathSharp.Geometry; |
| | | 10 | | using Gravitas.Colliders; |
| | | 11 | | using Gravitas.CollisionHandling; |
| | | 12 | | using SwiftCollections; |
| | | 13 | | using System; |
| | | 14 | | |
| | | 15 | | namespace Gravitas.Queries; |
| | | 16 | | |
| | | 17 | | internal readonly struct ConvexShape |
| | | 18 | | { |
| | | 19 | | internal enum ConvexShapeKind |
| | | 20 | | { |
| | | 21 | | Collider, |
| | | 22 | | Triangle, |
| | | 23 | | CircleSlab, |
| | | 24 | | Sphere |
| | | 25 | | } |
| | | 26 | | |
| | | 27 | | private readonly ConvexShapeKind _kind; |
| | | 28 | | private readonly LSCollider? _collider; |
| | | 29 | | private readonly LSMeshCollider? _triangleOwner; |
| | | 30 | | private readonly int _triangleIndex; |
| | | 31 | | private readonly Vector3d _offset; |
| | | 32 | | private readonly Vector3d _triangleA; |
| | | 33 | | private readonly Vector3d _triangleB; |
| | | 34 | | private readonly Vector3d _triangleC; |
| | | 35 | | private readonly Vector3d _center; |
| | | 36 | | private readonly Fixed64 _radius; |
| | | 37 | | private readonly Fixed64 _halfHeight; |
| | | 38 | | |
| | | 39 | | public ConvexShape(LSCollider collider, Vector3d offset) |
| | | 40 | | { |
| | 539 | 41 | | _kind = ConvexShapeKind.Collider; |
| | 539 | 42 | | _collider = collider; |
| | 539 | 43 | | _triangleOwner = null; |
| | 539 | 44 | | _triangleIndex = -1; |
| | 539 | 45 | | _offset = offset; |
| | 539 | 46 | | _triangleA = Vector3d.Zero; |
| | 539 | 47 | | _triangleB = Vector3d.Zero; |
| | 539 | 48 | | _triangleC = Vector3d.Zero; |
| | 539 | 49 | | _center = Vector3d.Zero; |
| | 539 | 50 | | _radius = Fixed64.Zero; |
| | 539 | 51 | | _halfHeight = Fixed64.Zero; |
| | 539 | 52 | | } |
| | | 53 | | |
| | | 54 | | public ConvexShape( |
| | | 55 | | LSMeshCollider triangleOwner, |
| | | 56 | | int triangleIndex, |
| | | 57 | | Vector3d triangleA, |
| | | 58 | | Vector3d triangleB, |
| | | 59 | | Vector3d triangleC) |
| | | 60 | | { |
| | 57 | 61 | | _kind = ConvexShapeKind.Triangle; |
| | 57 | 62 | | _collider = null; |
| | 57 | 63 | | _triangleOwner = triangleOwner; |
| | 57 | 64 | | _triangleIndex = triangleIndex; |
| | 57 | 65 | | _offset = Vector3d.Zero; |
| | 57 | 66 | | _triangleA = triangleA; |
| | 57 | 67 | | _triangleB = triangleB; |
| | 57 | 68 | | _triangleC = triangleC; |
| | 57 | 69 | | _center = Vector3d.Zero; |
| | 57 | 70 | | _radius = Fixed64.Zero; |
| | 57 | 71 | | _halfHeight = Fixed64.Zero; |
| | 57 | 72 | | } |
| | | 73 | | |
| | | 74 | | private ConvexShape( |
| | | 75 | | ConvexShapeKind kind, |
| | | 76 | | Vector3d center, |
| | | 77 | | Fixed64 radius, |
| | | 78 | | Fixed64 halfHeight, |
| | | 79 | | Vector3d offset) |
| | | 80 | | { |
| | 14634 | 81 | | _kind = kind; |
| | 14634 | 82 | | _collider = null; |
| | 14634 | 83 | | _triangleOwner = null; |
| | 14634 | 84 | | _triangleIndex = -1; |
| | 14634 | 85 | | _offset = offset; |
| | 14634 | 86 | | _triangleA = Vector3d.Zero; |
| | 14634 | 87 | | _triangleB = Vector3d.Zero; |
| | 14634 | 88 | | _triangleC = Vector3d.Zero; |
| | 14634 | 89 | | _center = center; |
| | 14634 | 90 | | _radius = radius; |
| | 14634 | 91 | | _halfHeight = halfHeight; |
| | 14634 | 92 | | } |
| | | 93 | | |
| | | 94 | | public bool ContainsCenter => |
| | 21 | 95 | | _kind != ConvexShapeKind.Collider || _collider is not LSMeshCollider; |
| | | 96 | | |
| | | 97 | | public FixedPointAnchor GetCenterAnchor() |
| | | 98 | | { |
| | 852 | 99 | | if (_kind == ConvexShapeKind.Triangle) |
| | | 100 | | { |
| | 85 | 101 | | return new FixedPointAnchor( |
| | 85 | 102 | | _triangleOwner!.Mesh.Origin, |
| | 85 | 103 | | _triangleOwner.Mesh.Rotation, |
| | 85 | 104 | | new Vector3d( |
| | 85 | 105 | | FixedMath.Average( |
| | 85 | 106 | | _triangleA.X, |
| | 85 | 107 | | _triangleB.X, |
| | 85 | 108 | | _triangleC.X), |
| | 85 | 109 | | FixedMath.Average( |
| | 85 | 110 | | _triangleA.Y, |
| | 85 | 111 | | _triangleB.Y, |
| | 85 | 112 | | _triangleC.Y), |
| | 85 | 113 | | FixedMath.Average( |
| | 85 | 114 | | _triangleA.Z, |
| | 85 | 115 | | _triangleB.Z, |
| | 85 | 116 | | _triangleC.Z))); |
| | | 117 | | } |
| | | 118 | | |
| | 767 | 119 | | return new FixedPointAnchor( |
| | 767 | 120 | | _kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere |
| | 767 | 121 | | ? _center |
| | 767 | 122 | | : _collider!.Center, |
| | 767 | 123 | | FixedQuaternion.Identity, |
| | 767 | 124 | | Vector3d.Zero, |
| | 767 | 125 | | _offset); |
| | | 126 | | } |
| | | 127 | | |
| | | 128 | | public static ConvexShape CreateCircleSlab(Vector3d center, Fixed64 radius, Fixed64 halfHeight) => |
| | 18 | 129 | | new(ConvexShapeKind.CircleSlab, center, radius, halfHeight, Vector3d.Zero); |
| | | 130 | | |
| | | 131 | | public static ConvexShape CreateSphere(Vector3d center, Fixed64 radius) => |
| | 14473 | 132 | | new(ConvexShapeKind.Sphere, center, radius, Fixed64.Zero, Vector3d.Zero); |
| | | 133 | | |
| | | 134 | | public void GetSourceBounds(out Vector3d min, out Vector3d max) => |
| | 14859 | 135 | | GetBounds(out min, out max); |
| | | 136 | | |
| | | 137 | | public void GetBounds(out Vector3d min, out Vector3d max) |
| | | 138 | | { |
| | 14888 | 139 | | if (_kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere) |
| | | 140 | | { |
| | 14565 | 141 | | Vector3d center = _center + _offset; |
| | 14565 | 142 | | Vector3d extents = _kind == ConvexShapeKind.Sphere |
| | 14565 | 143 | | ? Vector3d.One * _radius |
| | 14565 | 144 | | : new Vector3d(_radius, _halfHeight, _radius); |
| | 14565 | 145 | | min = center - extents; |
| | 14565 | 146 | | max = center + extents; |
| | 14565 | 147 | | return; |
| | | 148 | | } |
| | | 149 | | |
| | 323 | 150 | | if (_kind == ConvexShapeKind.Triangle) |
| | | 151 | | { |
| | 29 | 152 | | Vector3d localMin = Vector3d.Min( |
| | 29 | 153 | | _triangleA, |
| | 29 | 154 | | Vector3d.Min(_triangleB, _triangleC)); |
| | 29 | 155 | | Vector3d localMax = Vector3d.Max( |
| | 29 | 156 | | _triangleA, |
| | 29 | 157 | | Vector3d.Max(_triangleB, _triangleC)); |
| | 29 | 158 | | FixedBoundBox bounds = |
| | 29 | 159 | | FixedBoundBox.FromRelativeRotatedBoundsClippedToDomain( |
| | 29 | 160 | | _triangleOwner!.Mesh.Origin, |
| | 29 | 161 | | _triangleOwner.Mesh.Rotation, |
| | 29 | 162 | | localMin, |
| | 29 | 163 | | localMax, |
| | 29 | 164 | | Vector3d.Zero, |
| | 29 | 165 | | FixedQuaternion.Identity); |
| | 29 | 166 | | min = bounds.Min; |
| | 29 | 167 | | max = bounds.Max; |
| | 29 | 168 | | return; |
| | | 169 | | } |
| | | 170 | | |
| | 294 | 171 | | min = _collider!.Bounds.Min + _offset; |
| | 294 | 172 | | max = _collider.Bounds.Max + _offset; |
| | 294 | 173 | | } |
| | | 174 | | |
| | | 175 | | public bool CanTranslateCenter(Vector3d displacement) |
| | | 176 | | { |
| | 14834 | 177 | | Vector3d canonicalCenter = |
| | 14834 | 178 | | _kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere |
| | 14834 | 179 | | ? _center |
| | 14834 | 180 | | : _collider!.Center; |
| | 14834 | 181 | | return Vector3d.TryAdd( |
| | 14834 | 182 | | canonicalCenter, |
| | 14834 | 183 | | _offset, |
| | 14834 | 184 | | out Vector3d currentCenter) |
| | 14834 | 185 | | && Vector3d.TryAdd( |
| | 14834 | 186 | | currentCenter, |
| | 14834 | 187 | | displacement, |
| | 14834 | 188 | | out _); |
| | | 189 | | } |
| | | 190 | | |
| | | 191 | | public bool TryGetBoundsRelativeTo( |
| | | 192 | | Vector3d referenceOrigin, |
| | | 193 | | FixedQuaternion referenceRotation, |
| | | 194 | | out Vector3d min, |
| | | 195 | | out Vector3d max) |
| | | 196 | | { |
| | | 197 | | FixedBoundBox relativeBounds; |
| | 23 | 198 | | if (_kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere) |
| | | 199 | | { |
| | 11 | 200 | | Vector3d extents = _kind == ConvexShapeKind.Sphere |
| | 11 | 201 | | ? Vector3d.One * _radius |
| | 11 | 202 | | : new Vector3d(_radius, _halfHeight, _radius); |
| | 11 | 203 | | relativeBounds = |
| | 11 | 204 | | FixedBoundBox.FromRelativeRotatedBoundsClippedToDomain( |
| | 11 | 205 | | _center, |
| | 11 | 206 | | FixedQuaternion.Identity, |
| | 11 | 207 | | -extents, |
| | 11 | 208 | | extents, |
| | 11 | 209 | | referenceOrigin, |
| | 11 | 210 | | referenceRotation); |
| | | 211 | | } |
| | | 212 | | else |
| | | 213 | | { |
| | 12 | 214 | | relativeBounds = ColliderCanonicalBounds.GetRelativeBounds( |
| | 12 | 215 | | _collider!, |
| | 12 | 216 | | referenceOrigin, |
| | 12 | 217 | | referenceRotation); |
| | | 218 | | } |
| | | 219 | | |
| | 23 | 220 | | if (_offset == Vector3d.Zero) |
| | | 221 | | { |
| | 20 | 222 | | min = relativeBounds.Min; |
| | 20 | 223 | | max = relativeBounds.Max; |
| | 20 | 224 | | return true; |
| | | 225 | | } |
| | | 226 | | |
| | | 227 | | // Source offsets come from a chord whose magnitude was admitted by |
| | | 228 | | // Prepare, so a unit rotation cannot move them outside Fixed64. |
| | 3 | 229 | | _ = referenceRotation.Inverse().TryRotate( |
| | 3 | 230 | | _offset, |
| | 3 | 231 | | out Vector3d localOffset); |
| | 3 | 232 | | if (!Vector3d.TryAdd(relativeBounds.Min, localOffset, out min) |
| | 3 | 233 | | || !Vector3d.TryAdd(relativeBounds.Max, localOffset, out max)) |
| | | 234 | | { |
| | 2 | 235 | | min = default; |
| | 2 | 236 | | max = default; |
| | 2 | 237 | | return false; |
| | | 238 | | } |
| | | 239 | | |
| | 1 | 240 | | return true; |
| | | 241 | | } |
| | | 242 | | |
| | | 243 | | public ConvexShape WithSourceOffset(Vector3d additionalOffset) => |
| | 333 | 244 | | _kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere |
| | 333 | 245 | | ? new ConvexShape( |
| | 333 | 246 | | _kind, |
| | 333 | 247 | | _center, |
| | 333 | 248 | | _radius, |
| | 333 | 249 | | _halfHeight, |
| | 333 | 250 | | _offset + additionalOffset) |
| | 333 | 251 | | : new ConvexShape(_collider!, _offset + additionalOffset); |
| | | 252 | | |
| | | 253 | | public FixedPointAnchor GetSupportAnchor(Vector3d direction) |
| | | 254 | | { |
| | 2898 | 255 | | if (_kind == ConvexShapeKind.Triangle) |
| | | 256 | | { |
| | 215 | 257 | | return new FixedPointAnchor( |
| | 215 | 258 | | _triangleOwner!.Mesh.Origin, |
| | 215 | 259 | | _triangleOwner.Mesh.Rotation, |
| | 215 | 260 | | GetTriangleSupportLocalPoint(direction)); |
| | | 261 | | } |
| | | 262 | | |
| | 2683 | 263 | | if (_kind is ConvexShapeKind.CircleSlab or ConvexShapeKind.Sphere) |
| | | 264 | | { |
| | 657 | 265 | | if (_kind == ConvexShapeKind.Sphere) |
| | | 266 | | { |
| | 558 | 267 | | return FixedSegment.GetCenteredCapsuleSupportAnchor( |
| | 558 | 268 | | _center, |
| | 558 | 269 | | FixedQuaternion.Identity, |
| | 558 | 270 | | Fixed64.Zero, |
| | 558 | 271 | | _radius, |
| | 558 | 272 | | direction) |
| | 558 | 273 | | .WithLocalTranslation(_offset); |
| | | 274 | | } |
| | | 275 | | |
| | 99 | 276 | | return new FixedPointAnchor( |
| | 99 | 277 | | _center, |
| | 99 | 278 | | FixedQuaternion.Identity, |
| | 99 | 279 | | GetCircleSlabSupportLocalPoint(direction), |
| | 99 | 280 | | _offset); |
| | | 281 | | } |
| | | 282 | | |
| | 2026 | 283 | | return ConvexColliderSupport.GetSupportAnchor( |
| | 2026 | 284 | | _collider!, |
| | 2026 | 285 | | direction, |
| | 2026 | 286 | | _offset); |
| | | 287 | | } |
| | | 288 | | |
| | | 289 | | public FixedPointAnchor GetFallbackSurfaceAnchor(Vector3d direction) |
| | | 290 | | { |
| | 15 | 291 | | if (_kind == ConvexShapeKind.Collider |
| | 15 | 292 | | && _collider is LSCuboidCollider cuboid) |
| | | 293 | | { |
| | 11 | 294 | | Vector3d localDirection = |
| | 11 | 295 | | cuboid.Rotation.Inverse().Rotate(direction); |
| | 11 | 296 | | Vector3d absoluteDirection = Vector3d.Abs(localDirection); |
| | 11 | 297 | | Vector3d halfExtents = cuboid.OrientedBox.HalfExtents; |
| | | 298 | | Vector3d localPoint; |
| | 11 | 299 | | if (absoluteDirection.X >= absoluteDirection.Y |
| | 11 | 300 | | && absoluteDirection.X >= absoluteDirection.Z) |
| | | 301 | | { |
| | 4 | 302 | | localPoint = new Vector3d( |
| | 4 | 303 | | localDirection.X >= Fixed64.Zero |
| | 4 | 304 | | ? halfExtents.X |
| | 4 | 305 | | : -halfExtents.X, |
| | 4 | 306 | | Fixed64.Zero, |
| | 4 | 307 | | Fixed64.Zero); |
| | | 308 | | } |
| | 7 | 309 | | else if (absoluteDirection.Y >= absoluteDirection.Z) |
| | | 310 | | { |
| | 3 | 311 | | localPoint = new Vector3d( |
| | 3 | 312 | | Fixed64.Zero, |
| | 3 | 313 | | localDirection.Y >= Fixed64.Zero |
| | 3 | 314 | | ? halfExtents.Y |
| | 3 | 315 | | : -halfExtents.Y, |
| | 3 | 316 | | Fixed64.Zero); |
| | | 317 | | } |
| | | 318 | | else |
| | | 319 | | { |
| | 4 | 320 | | localPoint = new Vector3d( |
| | 4 | 321 | | Fixed64.Zero, |
| | 4 | 322 | | Fixed64.Zero, |
| | 4 | 323 | | localDirection.Z >= Fixed64.Zero |
| | 4 | 324 | | ? halfExtents.Z |
| | 4 | 325 | | : -halfExtents.Z); |
| | | 326 | | } |
| | | 327 | | |
| | 11 | 328 | | return new FixedPointAnchor( |
| | 11 | 329 | | cuboid.Center, |
| | 11 | 330 | | cuboid.Rotation, |
| | 11 | 331 | | localPoint, |
| | 11 | 332 | | ConvexColliderSupport.GetLocalDisplacement( |
| | 11 | 333 | | cuboid.Rotation, |
| | 11 | 334 | | _offset)); |
| | | 335 | | } |
| | | 336 | | |
| | 4 | 337 | | return GetSupportAnchor(direction); |
| | | 338 | | } |
| | | 339 | | |
| | | 340 | | public bool TryGetClosestPointOnSurface(Vector3d point, out Vector3d closest) |
| | | 341 | | { |
| | 72 | 342 | | SwiftThrowHelper.ThrowIfTrue( |
| | 72 | 343 | | _kind == ConvexShapeKind.Triangle, |
| | 72 | 344 | | nameof(ConvexShape), |
| | 72 | 345 | | "Triangle shapes are stationary mesh targets and cannot be sweep sources."); |
| | | 346 | | |
| | 71 | 347 | | if (_kind == ConvexShapeKind.Collider) |
| | | 348 | | { |
| | 55 | 349 | | if (_collider is LSMeshCollider) |
| | | 350 | | { |
| | 10 | 351 | | closest = Vector3d.Zero; |
| | 10 | 352 | | return false; |
| | | 353 | | } |
| | 45 | 354 | | if (_collider is LSCuboidCollider cuboid) |
| | | 355 | | { |
| | 21 | 356 | | if (!Vector3d.TrySubtract( |
| | 21 | 357 | | point, |
| | 21 | 358 | | _offset, |
| | 21 | 359 | | out Vector3d untranslatedPoint)) |
| | | 360 | | { |
| | 1 | 361 | | closest = Vector3d.Zero; |
| | 1 | 362 | | return false; |
| | | 363 | | } |
| | | 364 | | |
| | | 365 | | // Sweep offsets come from a chord whose magnitude was admitted |
| | | 366 | | // by Prepare, so a unit rotation preserves representability. |
| | 20 | 367 | | _ = cuboid.Rotation.Inverse().TryRotate( |
| | 20 | 368 | | _offset, |
| | 20 | 369 | | out Vector3d localTranslation); |
| | 20 | 370 | | return cuboid.OrientedBox |
| | 20 | 371 | | .GetClosestPointAnchor(untranslatedPoint) |
| | 20 | 372 | | .WithLocalTranslation(localTranslation) |
| | 20 | 373 | | .TryGetPoint(out closest); |
| | | 374 | | } |
| | 24 | 375 | | closest = _collider!.ClosestPointOnSurface(point - _offset) + _offset; |
| | 24 | 376 | | return true; |
| | | 377 | | } |
| | | 378 | | |
| | 16 | 379 | | Vector3d center = _center + _offset; |
| | 16 | 380 | | if (_kind == ConvexShapeKind.Sphere) |
| | | 381 | | { |
| | 2 | 382 | | Vector3d direction = point - center; |
| | 2 | 383 | | closest = center + GetSphereSupportLocalPoint(direction); |
| | 2 | 384 | | return true; |
| | | 385 | | } |
| | | 386 | | |
| | 14 | 387 | | Vector3d local = point - center; |
| | 14 | 388 | | Vector3d radial = new(local.X, Fixed64.Zero, local.Z); |
| | 14 | 389 | | Fixed64 radialDistance = radial.Magnitude; |
| | 14 | 390 | | Vector3d radialDirection = radialDistance > Fixed64.Epsilon |
| | 14 | 391 | | ? radial / radialDistance |
| | 14 | 392 | | : Vector3d.Right; |
| | 14 | 393 | | Fixed64 clampedY = FixedMath.Clamp(local.Y, -_halfHeight, _halfHeight); |
| | | 394 | | |
| | 14 | 395 | | if (radialDistance <= _radius && local.Y >= -_halfHeight && local.Y <= _halfHeight) |
| | | 396 | | { |
| | 6 | 397 | | Fixed64 sideDistance = _radius - radialDistance; |
| | 6 | 398 | | Fixed64 capDistance = _halfHeight - local.Y.Abs(); |
| | 6 | 399 | | closest = sideDistance <= capDistance |
| | 6 | 400 | | ? center + new Vector3d(radialDirection.X * _radius, local.Y, radialDirection.Z * _radius) |
| | 6 | 401 | | : center + new Vector3d(local.X, local.Y.Sign() * _halfHeight, local.Z); |
| | 6 | 402 | | return true; |
| | | 403 | | } |
| | | 404 | | |
| | 8 | 405 | | Fixed64 surfaceRadius = radialDistance > _radius ? _radius : radialDistance; |
| | 8 | 406 | | closest = center + new Vector3d( |
| | 8 | 407 | | radialDirection.X * surfaceRadius, |
| | 8 | 408 | | clampedY, |
| | 8 | 409 | | radialDirection.Z * surfaceRadius); |
| | 8 | 410 | | return true; |
| | | 411 | | } |
| | | 412 | | |
| | | 413 | | public bool TryGetPlanarSurfaceNormal(Vector3d point, out Vector3d normal) |
| | | 414 | | { |
| | 153 | 415 | | if (_kind == ConvexShapeKind.CircleSlab) |
| | | 416 | | { |
| | 1 | 417 | | throw new InvalidOperationException( |
| | 1 | 418 | | "Circle slabs are sweep sources and cannot be target shapes."); |
| | | 419 | | } |
| | 152 | 420 | | if (_kind == ConvexShapeKind.Sphere) |
| | | 421 | | { |
| | 1 | 422 | | throw new InvalidOperationException( |
| | 1 | 423 | | "Sphere query sources cannot be target shapes."); |
| | | 424 | | } |
| | | 425 | | |
| | 151 | 426 | | if (_kind == ConvexShapeKind.Triangle) |
| | | 427 | | { |
| | 24 | 428 | | normal = _triangleOwner!.Mesh.GetFaceNormalWorld(_triangleIndex); |
| | 24 | 429 | | return true; |
| | | 430 | | } |
| | | 431 | | |
| | 127 | 432 | | return _collider!.TryGetPlanarSurfaceNormal(point, out normal); |
| | | 433 | | } |
| | | 434 | | |
| | | 435 | | private Vector3d GetCircleSlabSupportLocalPoint(Vector3d direction) |
| | | 436 | | { |
| | 99 | 437 | | Vector3d radial = new(direction.X, Fixed64.Zero, direction.Z); |
| | 99 | 438 | | Fixed64 radialMagnitude = radial.Magnitude; |
| | 99 | 439 | | Vector3d radialSupport = radialMagnitude > Fixed64.Epsilon |
| | 99 | 440 | | ? radial / radialMagnitude * _radius |
| | 99 | 441 | | : Vector3d.Right * _radius; |
| | 99 | 442 | | Fixed64 y = direction.Y >= Fixed64.Zero ? _halfHeight : -_halfHeight; |
| | 99 | 443 | | return new Vector3d(radialSupport.X, y, radialSupport.Z); |
| | | 444 | | } |
| | | 445 | | |
| | | 446 | | private Vector3d GetSphereSupportLocalPoint(Vector3d direction) |
| | | 447 | | { |
| | 2 | 448 | | Vector3d normal = direction.IsZero |
| | 2 | 449 | | ? Vector3d.Right |
| | 2 | 450 | | : direction.Normalized; |
| | 2 | 451 | | return normal * _radius; |
| | | 452 | | } |
| | | 453 | | |
| | | 454 | | private Vector3d GetTriangleSupportLocalPoint(Vector3d direction) |
| | | 455 | | { |
| | 215 | 456 | | Vector3d localDirection = |
| | 215 | 457 | | _triangleOwner!.Mesh.Rotation.Inverse().Rotate(direction); |
| | 215 | 458 | | Vector3d best = _triangleA; |
| | 215 | 459 | | if (Vector3d.CompareProjection(_triangleB, best, localDirection) > 0) |
| | 51 | 460 | | best = _triangleB; |
| | | 461 | | |
| | 215 | 462 | | if (Vector3d.CompareProjection(_triangleC, best, localDirection) > 0) |
| | 56 | 463 | | best = _triangleC; |
| | | 464 | | |
| | 215 | 465 | | return best; |
| | | 466 | | } |
| | | 467 | | } |